aboutsummaryrefslogtreecommitdiff
path: root/fs/xfs/scrub/ialloc_repair.c
blob: a00ec7ae17925d9c38066764014fee7d91491e7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2018-2023 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <djwong@kernel.org>
 */
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_defer.h"
#include "xfs_btree.h"
#include "xfs_btree_staging.h"
#include "xfs_bit.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_inode.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_ialloc_btree.h"
#include "xfs_icache.h"
#include "xfs_rmap.h"
#include "xfs_rmap_btree.h"
#include "xfs_log.h"
#include "xfs_trans_priv.h"
#include "xfs_error.h"
#include "xfs_health.h"
#include "xfs_ag.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/btree.h"
#include "scrub/trace.h"
#include "scrub/repair.h"
#include "scrub/bitmap.h"
#include "scrub/agb_bitmap.h"
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
#include "scrub/newbt.h"
#include "scrub/reap.h"

/*
 * Inode Btree Repair
 * ==================
 *
 * A quick refresher of inode btrees on a v5 filesystem:
 *
 * - Inode records are read into memory in units of 'inode clusters'.  However
 *   many inodes fit in a cluster buffer is the smallest number of inodes that
 *   can be allocated or freed.  Clusters are never smaller than one fs block
 *   though they can span multiple blocks.  The size (in fs blocks) is
 *   computed with xfs_icluster_size_fsb().  The fs block alignment of a
 *   cluster is computed with xfs_ialloc_cluster_alignment().
 *
 * - Each inode btree record can describe a single 'inode chunk'.  The chunk
 *   size is defined to be 64 inodes.  If sparse inodes are enabled, every
 *   inobt record must be aligned to the chunk size; if not, every record must
 *   be aligned to the start of a cluster.  It is possible to construct an XFS
 *   geometry where one inobt record maps to multiple inode clusters; it is
 *   also possible to construct a geometry where multiple inobt records map to
 *   different parts of one inode cluster.
 *
 * - If sparse inodes are not enabled, the smallest unit of allocation for
 *   inode records is enough to contain one inode chunk's worth of inodes.
 *
 * - If sparse inodes are enabled, the holemask field will be active.  Each
 *   bit of the holemask represents 4 potential inodes; if set, the
 *   corresponding space does *not* contain inodes and must be left alone.
 *   Clusters cannot be smaller than 4 inodes.  The smallest unit of allocation
 *   of inode records is one inode cluster.
 *
 * So what's the rebuild algorithm?
 *
 * Iterate the reverse mapping records looking for OWN_INODES and OWN_INOBT
 * records.  The OWN_INOBT records are the old inode btree blocks and will be
 * cleared out after we've rebuilt the tree.  Each possible inode cluster
 * within an OWN_INODES record will be read in; for each possible inobt record
 * associated with that cluster, compute the freemask calculated from the
 * i_mode data in the inode chunk.  For sparse inodes the holemask will be
 * calculated by creating the properly aligned inobt record and punching out
 * any chunk that's missing.  Inode allocations and frees grab the AGI first,
 * so repair protects itself from concurrent access by locking the AGI.
 *
 * Once we've reconstructed all the inode records, we can create new inode
 * btree roots and reload the btrees.  We rebuild both inode trees at the same
 * time because they have the same rmap owner and it would be more complex to
 * figure out if the other tree isn't in need of a rebuild and which OWN_INOBT
 * blocks it owns.  We have all the data we need to build both, so dump
 * everything and start over.
 *
 * We use the prefix 'xrep_ibt' because we rebuild both inode btrees at once.
 */

struct xrep_ibt {
	/* Record under construction. */
	struct xfs_inobt_rec_incore	rie;

	/* new inobt information */
	struct xrep_newbt	new_inobt;

	/* new finobt information */
	struct xrep_newbt	new_finobt;

	/* Old inode btree blocks we found in the rmap. */
	struct xagb_bitmap	old_iallocbt_blocks;

	/* Reconstructed inode records. */
	struct xfarray		*inode_records;

	struct xfs_scrub	*sc;

	/* Number of inodes assigned disk space. */
	unsigned int		icount;

	/* Number of inodes in use. */
	unsigned int		iused;

	/* Number of finobt records needed. */
	unsigned int		finobt_recs;

	/* get_records()'s position in the inode record array. */
	xfarray_idx_t		array_cur;
};

/*
 * Is this inode in use?  If the inode is in memory we can tell from i_mode,
 * otherwise we have to check di_mode in the on-disk buffer.  We only care
 * that the high (i.e. non-permission) bits of _mode are zero.  This should be
 * safe because repair keeps all AG headers locked until the end, and process
 * trying to perform an inode allocation/free must lock the AGI.
 *
 * @cluster_ag_base is the inode offset of the cluster within the AG.
 * @cluster_bp is the cluster buffer.
 * @cluster_index is the inode offset within the inode cluster.
 */
STATIC int
xrep_ibt_check_ifree(
	struct xrep_ibt		*ri,
	xfs_agino_t		cluster_ag_base,
	struct xfs_buf		*cluster_bp,
	unsigned int		cluster_index,
	bool			*inuse)
{
	struct xfs_scrub	*sc = ri->sc;
	struct xfs_mount	*mp = sc->mp;
	struct xfs_dinode	*dip;
	xfs_ino_t		fsino;
	xfs_agino_t		agino;
	xfs_agnumber_t		agno = ri->sc->sa.pag->pag_agno;
	unsigned int		cluster_buf_base;
	unsigned int		offset;
	int			error;

	agino = cluster_ag_base + cluster_index;
	fsino = XFS_AGINO_TO_INO(mp, agno, agino);

	/* Inode uncached or half assembled, read disk buffer */
	cluster_buf_base = XFS_INO_TO_OFFSET(mp, cluster_ag_base);
	offset = (cluster_buf_base + cluster_index) * mp->m_sb.sb_inodesize;
	if (offset >= BBTOB(cluster_bp->b_length))
		return -EFSCORRUPTED;
	dip = xfs_buf_offset(cluster_bp, offset);
	if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC)
		return -EFSCORRUPTED;

	if (dip->di_version >= 3 && be64_to_cpu(dip->di_ino) != fsino)
		return -EFSCORRUPTED;

	/* Will the in-core inode tell us if it's in use? */
	error = xchk_inode_is_allocated(sc, agino, inuse);
	if (!error)
		return 0;

	*inuse = dip->di_mode != 0;
	return 0;
}

/* Stash the accumulated inobt record for rebuilding. */
STATIC int
xrep_ibt_stash(
	struct xrep_ibt		*ri)
{
	int			error = 0;

	if (xchk_should_terminate(ri->sc, &error))
		return error;

	ri->rie.ir_freecount = xfs_inobt_rec_freecount(&ri->rie);
	if (xfs_inobt_check_irec(ri->sc->sa.pag, &ri->rie) != NULL)
		return -EFSCORRUPTED;

	if (ri->rie.ir_freecount > 0)
		ri->finobt_recs++;

	trace_xrep_ibt_found(ri->sc->mp, ri->sc->sa.pag->pag_agno, &ri->rie);

	error = xfarray_append(ri->inode_records, &ri->rie);
	if (error)
		return error;

	ri->rie.ir_startino = NULLAGINO;
	return 0;
}

/*
 * Given an extent of inodes and an inode cluster buffer, calculate the
 * location of the corresponding inobt record (creating it if necessary),
 * then update the parts of the holemask and freemask of that record that
 * correspond to the inode extent we were given.
 *
 * @cluster_ir_startino is the AG inode number of an inobt record that we're
 * proposing to create for this inode cluster.  If sparse inodes are enabled,
 * we must round down to a chunk boundary to find the actual sparse record.
 * @cluster_bp is the buffer of the inode cluster.
 * @nr_inodes is the number of inodes to check from the cluster.
 */
STATIC int
xrep_ibt_cluster_record(
	struct xrep_ibt		*ri,
	xfs_agino_t		cluster_ir_startino,
	struct xfs_buf		*cluster_bp,
	unsigned int		nr_inodes)
{
	struct xfs_scrub	*sc = ri->sc;
	struct xfs_mount	*mp = sc->mp;
	xfs_agino_t		ir_startino;
	unsigned int		cluster_base;
	unsigned int		cluster_index;
	int			error = 0;

	ir_startino = cluster_ir_startino;
	if (xfs_has_sparseinodes(mp))
		ir_startino = rounddown(ir_startino, XFS_INODES_PER_CHUNK);
	cluster_base = cluster_ir_startino - ir_startino;

	/*
	 * If the accumulated inobt record doesn't map this cluster, add it to
	 * the list and reset it.
	 */
	if (ri->rie.ir_startino != NULLAGINO &&
	    ri->rie.ir_startino + XFS_INODES_PER_CHUNK <= ir_startino) {
		error = xrep_ibt_stash(ri);
		if (error)
			return error;
	}

	if (ri->rie.ir_startino == NULLAGINO) {
		ri->rie.ir_startino = ir_startino;
		ri->rie.ir_free = XFS_INOBT_ALL_FREE;
		ri->rie.ir_holemask = 0xFFFF;
		ri->rie.ir_count = 0;
	}

	/* Record the whole cluster. */
	ri->icount += nr_inodes;
	ri->rie.ir_count += nr_inodes;
	ri->rie.ir_holemask &= ~xfs_inobt_maskn(
				cluster_base / XFS_INODES_PER_HOLEMASK_BIT,
				nr_inodes / XFS_INODES_PER_HOLEMASK_BIT);

	/* Which inodes within this cluster are free? */
	for (cluster_index = 0; cluster_index < nr_inodes; cluster_index++) {
		bool		inuse = false;

		error = xrep_ibt_check_ifree(ri, cluster_ir_startino,
				cluster_bp, cluster_index, &inuse);
		if (error)
			return error;
		if (!inuse)
			continue;
		ri->iused++;
		ri->rie.ir_free &= ~XFS_INOBT_MASK(cluster_base +
						   cluster_index);
	}
	return 0;
}

/*
 * For each inode cluster covering the physical extent recorded by the rmapbt,
 * we must calculate the properly aligned startino of that cluster, then
 * iterate each cluster to fill in used and filled masks appropriately.  We
 * then use the (startino, used, filled) information to construct the
 * appropriate inode records.
 */
STATIC int
xrep_ibt_process_cluster(
	struct xrep_ibt		*ri,
	xfs_agblock_t		cluster_bno)
{
	struct xfs_imap		imap;
	struct xfs_buf		*cluster_bp;
	struct xfs_scrub	*sc = ri->sc;
	struct xfs_mount	*mp = sc->mp;
	struct xfs_ino_geometry	*igeo = M_IGEO(mp);
	xfs_agino_t		cluster_ag_base;
	xfs_agino_t		irec_index;
	unsigned int		nr_inodes;
	int			error;

	nr_inodes = min_t(unsigned int, igeo->inodes_per_cluster,
			XFS_INODES_PER_CHUNK);

	/*
	 * Grab the inode cluster buffer.  This is safe to do with a broken
	 * inobt because imap_to_bp directly maps the buffer without touching
	 * either inode btree.
	 */
	imap.im_blkno = XFS_AGB_TO_DADDR(mp, sc->sa.pag->pag_agno, cluster_bno);
	imap.im_len = XFS_FSB_TO_BB(mp, igeo->blocks_per_cluster);
	imap.im_boffset = 0;
	error = xfs_imap_to_bp(mp, sc->tp, &imap, &cluster_bp);
	if (error)
		return error;

	/*
	 * Record the contents of each possible inobt record mapping this
	 * cluster.
	 */
	cluster_ag_base = XFS_AGB_TO_AGINO(mp, cluster_bno);
	for (irec_index = 0;
	     irec_index < igeo->inodes_per_cluster;
	     irec_index += XFS_INODES_PER_CHUNK) {
		error = xrep_ibt_cluster_record(ri,
				cluster_ag_base + irec_index, cluster_bp,
				nr_inodes);
		if (error)
			break;

	}

	xfs_trans_brelse(sc->tp, cluster_bp);
	return error;
}

/* Check for any obvious conflicts in the inode chunk extent. */
STATIC int
xrep_ibt_check_inode_ext(
	struct xfs_scrub	*sc,
	xfs_agblock_t		agbno,
	xfs_extlen_t		len)
{
	struct xfs_mount	*mp = sc->mp;
	struct xfs_ino_geometry	*igeo = M_IGEO(mp);
	xfs_agino_t		agino;
	enum xbtree_recpacking	outcome;
	int			error;

	/* Inode records must be within the AG. */
	if (!xfs_verify_agbext(sc->sa.pag, agbno, len))
		return -EFSCORRUPTED;

	/* The entire record must align to the inode cluster size. */
	if (!IS_ALIGNED(agbno, igeo->blocks_per_cluster) ||
	    !IS_ALIGNED(agbno + len, igeo->blocks_per_cluster))
		return -EFSCORRUPTED;

	/*
	 * The entire record must also adhere to the inode cluster alignment
	 * size if sparse inodes are not enabled.
	 */
	if (!xfs_has_sparseinodes(mp) &&
	    (!IS_ALIGNED(agbno, igeo->cluster_align) ||
	     !IS_ALIGNED(agbno + len, igeo->cluster_align)))
		return -EFSCORRUPTED;

	/*
	 * On a sparse inode fs, this cluster could be part of a sparse chunk.
	 * Sparse clusters must be aligned to sparse chunk alignment.
	 */
	if (xfs_has_sparseinodes(mp) && mp->m_sb.sb_spino_align &&
	    (!IS_ALIGNED(agbno, mp->m_sb.sb_spino_align) ||
	     !IS_ALIGNED(agbno + len, mp->m_sb.sb_spino_align)))
		return -EFSCORRUPTED;

	/* Make sure the entire range of blocks are valid AG inodes. */
	agino = XFS_AGB_TO_AGINO(mp, agbno);
	if (!xfs_verify_agino(sc->sa.pag, agino))
		return -EFSCORRUPTED;

	agino = XFS_AGB_TO_AGINO(mp, agbno + len) - 1;
	if (!xfs_verify_agino(sc->sa.pag, agino))
		return -EFSCORRUPTED;

	/* Make sure this isn't free space. */
	error = xfs_alloc_has_records(sc->sa.bno_cur, agbno, len, &outcome);
	if (error)
		return error;
	if (outcome != XBTREE_RECPACKING_EMPTY)
		return -EFSCORRUPTED;

	return 0;
}

/* Found a fragment of the old inode btrees; dispose of them later. */
STATIC int
xrep_ibt_record_old_btree_blocks(
	struct xrep_ibt			*ri,
	const struct xfs_rmap_irec	*rec)
{
	if (!xfs_verify_agbext(ri->sc->sa.pag, rec->rm_startblock,
				rec->rm_blockcount))
		return -EFSCORRUPTED;

	return xagb_bitmap_set(&ri->old_iallocbt_blocks, rec->rm_startblock,
			rec->rm_blockcount);
}

/* Record extents that belong to inode cluster blocks. */
STATIC int
xrep_ibt_record_inode_blocks(
	struct xrep_ibt			*ri,
	const struct xfs_rmap_irec	*rec)
{
	struct xfs_mount		*mp = ri->sc->mp;
	struct xfs_ino_geometry		*igeo = M_IGEO(mp);
	xfs_agblock_t			cluster_base;
	int				error;

	error = xrep_ibt_check_inode_ext(ri->sc, rec->rm_startblock,
			rec->rm_blockcount);
	if (error)
		return error;

	trace_xrep_ibt_walk_rmap(mp, ri->sc->sa.pag->pag_agno,
			rec->rm_startblock, rec->rm_blockcount, rec->rm_owner,
			rec->rm_offset, rec->rm_flags);

	/*
	 * Record the free/hole masks for each inode cluster that could be
	 * mapped by this rmap record.
	 */
	for (cluster_base = 0;
	     cluster_base < rec->rm_blockcount;
	     cluster_base += igeo->blocks_per_cluster) {
		error = xrep_ibt_process_cluster(ri,
				rec->rm_startblock + cluster_base);
		if (error)
			return error;
	}

	return 0;
}

STATIC int
xrep_ibt_walk_rmap(
	struct xfs_btree_cur		*cur,
	const struct xfs_rmap_irec	*rec,
	void				*priv)
{
	struct xrep_ibt			*ri = priv;
	int				error = 0;

	if (xchk_should_terminate(ri->sc, &error))
		return error;

	switch (rec->rm_owner) {
	case XFS_RMAP_OWN_INOBT:
		return xrep_ibt_record_old_btree_blocks(ri, rec);
	case XFS_RMAP_OWN_INODES:
		return xrep_ibt_record_inode_blocks(ri, rec);
	}
	return 0;
}

/*
 * Iterate all reverse mappings to find the inodes (OWN_INODES) and the inode
 * btrees (OWN_INOBT).  Figure out if we have enough free space to reconstruct
 * the inode btrees.  The caller must clean up the lists if anything goes
 * wrong.
 */
STATIC int
xrep_ibt_find_inodes(
	struct xrep_ibt		*ri)
{
	struct xfs_scrub	*sc = ri->sc;
	int			error;

	ri->rie.ir_startino = NULLAGINO;

	/* Collect all reverse mappings for inode blocks. */
	xrep_ag_btcur_init(sc, &sc->sa);
	error = xfs_rmap_query_all(sc->sa.rmap_cur, xrep_ibt_walk_rmap, ri);
	xchk_ag_btcur_free(&sc->sa);
	if (error)
		return error;

	/* If we have a record ready to go, add it to the array. */
	if (ri->rie.ir_startino != NULLAGINO)
		return xrep_ibt_stash(ri);

	return 0;
}

/* Update the AGI counters. */
STATIC int
xrep_ibt_reset_counters(
	struct xrep_ibt		*ri)
{
	struct xfs_scrub	*sc = ri->sc;
	struct xfs_agi		*agi = sc->sa.agi_bp->b_addr;
	unsigned int		freecount = ri->icount - ri->iused;

	/* Trigger inode count recalculation */
	xfs_force_summary_recalc(sc->mp);

	/*
	 * The AGI header contains extra information related to the inode
	 * btrees, so we must update those fields here.
	 */
	agi->agi_count = cpu_to_be32(ri->icount);
	agi->agi_freecount = cpu_to_be32(freecount);
	xfs_ialloc_log_agi(sc->tp, sc->sa.agi_bp,
			   XFS_AGI_COUNT | XFS_AGI_FREECOUNT);

	/* Reinitialize with the values we just logged. */
	return xrep_reinit_pagi(sc);
}

/* Retrieve finobt data for bulk load. */
STATIC int
xrep_fibt_get_records(
	struct xfs_btree_cur		*cur,
	unsigned int			idx,
	struct xfs_btree_block		*block,
	unsigned int			nr_wanted,
	void				*priv)
{
	struct xfs_inobt_rec_incore	*irec = &cur->bc_rec.i;
	struct xrep_ibt			*ri = priv;
	union xfs_btree_rec		*block_rec;
	unsigned int			loaded;
	int				error;

	for (loaded = 0; loaded < nr_wanted; loaded++, idx++) {
		do {
			error = xfarray_load(ri->inode_records,
					ri->array_cur++, irec);
		} while (error == 0 && xfs_inobt_rec_freecount(irec) == 0);
		if (error)
			return error;

		block_rec = xfs_btree_rec_addr(cur, idx, block);
		cur->bc_ops->init_rec_from_cur(cur, block_rec);
	}

	return loaded;
}

/* Retrieve inobt data for bulk load. */
STATIC int
xrep_ibt_get_records(
	struct xfs_btree_cur		*cur,
	unsigned int			idx,
	struct xfs_btree_block		*block,
	unsigned int			nr_wanted,
	void				*priv)
{
	struct xfs_inobt_rec_incore	*irec = &cur->bc_rec.i;
	struct xrep_ibt			*ri = priv;
	union xfs_btree_rec		*block_rec;
	unsigned int			loaded;
	int				error;

	for (loaded = 0; loaded < nr_wanted; loaded++, idx++) {
		error = xfarray_load(ri->inode_records, ri->array_cur++, irec);
		if (error)
			return error;

		block_rec = xfs_btree_rec_addr(cur, idx, block);
		cur->bc_ops->init_rec_from_cur(cur, block_rec);
	}

	return loaded;
}

/* Feed one of the new inobt blocks to the bulk loader. */
STATIC int
xrep_ibt_claim_block(
	struct xfs_btree_cur	*cur,
	union xfs_btree_ptr	*ptr,
	void			*priv)
{
	struct xrep_ibt		*ri = priv;

	return xrep_newbt_claim_block(cur, &ri->new_inobt, ptr);
}

/* Feed one of the new finobt blocks to the bulk loader. */
STATIC int
xrep_fibt_claim_block(
	struct xfs_btree_cur	*cur,
	union xfs_btree_ptr	*ptr,
	void			*priv)
{
	struct xrep_ibt		*ri = priv;

	return xrep_newbt_claim_block(cur, &ri->new_finobt, ptr);
}

/* Make sure the records do not overlap in inumber address space. */
STATIC int
xrep_ibt_check_overlap(
	struct xrep_ibt			*ri)
{
	struct xfs_inobt_rec_incore	irec;
	xfarray_idx_t			cur;
	xfs_agino_t			next_agino = 0;
	int				error = 0;

	foreach_xfarray_idx(ri->inode_records, cur) {
		if (xchk_should_terminate(ri->sc, &error))
			return error;

		error = xfarray_load(ri->inode_records, cur, &irec);
		if (error)
			return error;

		if (irec.ir_startino < next_agino)
			return -EFSCORRUPTED;

		next_agino = irec.ir_startino + XFS_INODES_PER_CHUNK;
	}

	return error;
}

/* Build new inode btrees and dispose of the old one. */
STATIC int
xrep_ibt_build_new_trees(
	struct xrep_ibt		*ri)
{
	struct xfs_scrub	*sc = ri->sc;
	struct xfs_btree_cur	*ino_cur;
	struct xfs_btree_cur	*fino_cur = NULL;
	xfs_fsblock_t		fsbno;
	bool			need_finobt;
	int			error;

	need_finobt = xfs_has_finobt(sc->mp);

	/*
	 * Create new btrees for staging all the inobt records we collected
	 * earlier.  The records were collected in order of increasing agino,
	 * so we do not have to sort them.  Ensure there are no overlapping
	 * records.
	 */
	error = xrep_ibt_check_overlap(ri);
	if (error)
		return error;

	/*
	 * The new inode btrees will not be rooted in the AGI until we've
	 * successfully rebuilt the tree.
	 *
	 * Start by setting up the inobt staging cursor.
	 */
	fsbno = XFS_AGB_TO_FSB(sc->mp, sc->sa.pag->pag_agno,
			XFS_IBT_BLOCK(sc->mp)),
	xrep_newbt_init_ag(&ri->new_inobt, sc, &XFS_RMAP_OINFO_INOBT, fsbno,
			XFS_AG_RESV_NONE);
	ri->new_inobt.bload.claim_block = xrep_ibt_claim_block;
	ri->new_inobt.bload.get_records = xrep_ibt_get_records;

	ino_cur = xfs_inobt_init_cursor(sc->sa.pag, NULL, NULL);
	xfs_btree_stage_afakeroot(ino_cur, &ri->new_inobt.afake);
	error = xfs_btree_bload_compute_geometry(ino_cur, &ri->new_inobt.bload,
			xfarray_length(ri->inode_records));
	if (error)
		goto err_inocur;

	/* Set up finobt staging cursor. */
	if (need_finobt) {
		enum xfs_ag_resv_type	resv = XFS_AG_RESV_METADATA;

		if (sc->mp->m_finobt_nores)
			resv = XFS_AG_RESV_NONE;

		fsbno = XFS_AGB_TO_FSB(sc->mp, sc->sa.pag->pag_agno,
				XFS_FIBT_BLOCK(sc->mp)),
		xrep_newbt_init_ag(&ri->new_finobt, sc, &XFS_RMAP_OINFO_INOBT,
				fsbno, resv);
		ri->new_finobt.bload.claim_block = xrep_fibt_claim_block;
		ri->new_finobt.bload.get_records = xrep_fibt_get_records;

		fino_cur = xfs_finobt_init_cursor(sc->sa.pag, NULL, NULL);
		xfs_btree_stage_afakeroot(fino_cur, &ri->new_finobt.afake);
		error = xfs_btree_bload_compute_geometry(fino_cur,
				&ri->new_finobt.bload, ri->finobt_recs);
		if (error)
			goto err_finocur;
	}

	/* Last chance to abort before we start committing fixes. */
	if (xchk_should_terminate(sc, &error))
		goto err_finocur;

	/* Reserve all the space we need to build the new btrees. */
	error = xrep_newbt_alloc_blocks(&ri->new_inobt,
			ri->new_inobt.bload.nr_blocks);
	if (error)
		goto err_finocur;

	if (need_finobt) {
		error = xrep_newbt_alloc_blocks(&ri->new_finobt,
				ri->new_finobt.bload.nr_blocks);
		if (error)
			goto err_finocur;
	}

	/* Add all inobt records. */
	ri->array_cur = XFARRAY_CURSOR_INIT;
	error = xfs_btree_bload(ino_cur, &ri->new_inobt.bload, ri);
	if (error)
		goto err_finocur;

	/* Add all finobt records. */
	if (need_finobt) {
		ri->array_cur = XFARRAY_CURSOR_INIT;
		error = xfs_btree_bload(fino_cur, &ri->new_finobt.bload, ri);
		if (error)
			goto err_finocur;
	}

	/*
	 * Install the new btrees in the AG header.  After this point the old
	 * btrees are no longer accessible and the new trees are live.
	 */
	xfs_inobt_commit_staged_btree(ino_cur, sc->tp, sc->sa.agi_bp);
	xfs_btree_del_cursor(ino_cur, 0);

	if (fino_cur) {
		xfs_inobt_commit_staged_btree(fino_cur, sc->tp, sc->sa.agi_bp);
		xfs_btree_del_cursor(fino_cur, 0);
	}

	/* Reset the AGI counters now that we've changed the inode roots. */
	error = xrep_ibt_reset_counters(ri);
	if (error)
		goto err_finobt;

	/* Free unused blocks and bitmap. */
	if (need_finobt) {
		error = xrep_newbt_commit(&ri->new_finobt);
		if (error)
			goto err_inobt;
	}
	error = xrep_newbt_commit(&ri->new_inobt);
	if (error)
		return error;

	return xrep_roll_ag_trans(sc);

err_finocur:
	if (need_finobt)
		xfs_btree_del_cursor(fino_cur, error);
err_inocur:
	xfs_btree_del_cursor(ino_cur, error);
err_finobt:
	if (need_finobt)
		xrep_newbt_cancel(&ri->new_finobt);
err_inobt:
	xrep_newbt_cancel(&ri->new_inobt);
	return error;
}

/*
 * Now that we've logged the roots of the new btrees, invalidate all of the
 * old blocks and free them.
 */
STATIC int
xrep_ibt_remove_old_trees(
	struct xrep_ibt		*ri)
{
	struct xfs_scrub	*sc = ri->sc;
	int			error;

	/*
	 * Free the old inode btree blocks if they're not in use.  It's ok to
	 * reap with XFS_AG_RESV_NONE even if the finobt had a per-AG
	 * reservation because we reset the reservation before releasing the
	 * AGI and AGF header buffer locks.
	 */
	error = xrep_reap_agblocks(sc, &ri->old_iallocbt_blocks,
			&XFS_RMAP_OINFO_INOBT, XFS_AG_RESV_NONE);
	if (error)
		return error;

	/*
	 * If the finobt is enabled and has a per-AG reservation, make sure we
	 * reinitialize the per-AG reservations.
	 */
	if (xfs_has_finobt(sc->mp) && !sc->mp->m_finobt_nores)
		sc->flags |= XREP_RESET_PERAG_RESV;

	return 0;
}

/* Repair both inode btrees. */
int
xrep_iallocbt(
	struct xfs_scrub	*sc)
{
	struct xrep_ibt		*ri;
	struct xfs_mount	*mp = sc->mp;
	char			*descr;
	xfs_agino_t		first_agino, last_agino;
	int			error = 0;

	/* We require the rmapbt to rebuild anything. */
	if (!xfs_has_rmapbt(mp))
		return -EOPNOTSUPP;

	ri = kzalloc(sizeof(struct xrep_ibt), XCHK_GFP_FLAGS);
	if (!ri)
		return -ENOMEM;
	ri->sc = sc;

	/* We rebuild both inode btrees. */
	sc->sick_mask = XFS_SICK_AG_INOBT | XFS_SICK_AG_FINOBT;

	/* Set up enough storage to handle an AG with nothing but inodes. */
	xfs_agino_range(mp, sc->sa.pag->pag_agno, &first_agino, &last_agino);
	last_agino /= XFS_INODES_PER_CHUNK;
	descr = xchk_xfile_ag_descr(sc, "inode index records");
	error = xfarray_create(descr, last_agino,
			sizeof(struct xfs_inobt_rec_incore),
			&ri->inode_records);
	kfree(descr);
	if (error)
		goto out_ri;

	/* Collect the inode data and find the old btree blocks. */
	xagb_bitmap_init(&ri->old_iallocbt_blocks);
	error = xrep_ibt_find_inodes(ri);
	if (error)
		goto out_bitmap;

	/* Rebuild the inode indexes. */
	error = xrep_ibt_build_new_trees(ri);
	if (error)
		goto out_bitmap;

	/* Kill the old tree. */
	error = xrep_ibt_remove_old_trees(ri);
	if (error)
		goto out_bitmap;

out_bitmap:
	xagb_bitmap_destroy(&ri->old_iallocbt_blocks);
	xfarray_destroy(ri->inode_records);
out_ri:
	kfree(ri);
	return error;
}

/* Make sure both btrees are ok after we've rebuilt them. */
int
xrep_revalidate_iallocbt(
	struct xfs_scrub	*sc)
{
	__u32			old_type = sc->sm->sm_type;
	int			error;

	/*
	 * We must update sm_type temporarily so that the tree-to-tree cross
	 * reference checks will work in the correct direction, and also so
	 * that tracing will report correctly if there are more errors.
	 */
	sc->sm->sm_type = XFS_SCRUB_TYPE_INOBT;
	error = xchk_iallocbt(sc);
	if (error)
		goto out;

	if (xfs_has_finobt(sc->mp)) {
		sc->sm->sm_type = XFS_SCRUB_TYPE_FINOBT;
		error = xchk_iallocbt(sc);
	}

out:
	sc->sm->sm_type = old_type;
	return error;
}