File: repair.c

package info (click to toggle)
xfsprogs 6.17.0-2
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 11,324 kB
  • sloc: ansic: 167,334; sh: 4,604; makefile: 1,336; python: 835; cpp: 5
file content (898 lines) | stat: -rw-r--r-- 23,867 bytes parent folder | download
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
885
886
887
888
889
890
891
892
893
894
895
896
897
898
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2018-2024 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <djwong@kernel.org>
 */
#include "xfs.h"
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include "list.h"
#include "libfrog/paths.h"
#include "libfrog/fsgeom.h"
#include "libfrog/scrub.h"
#include "xfs_scrub.h"
#include "common.h"
#include "scrub.h"
#include "progress.h"
#include "repair.h"
#include "descr.h"
#include "scrub_private.h"

/* General repair routines. */

/*
 * Bitmap showing the correctness dependencies between scrub types for repairs.
 * There are no edges between AG btrees and AG headers because we can't mount
 * the filesystem if the btree root pointers in the AG headers are wrong.
 * Dependencies cannot cross scrub groups.
 */
#define DEP(x) (1U << (x))
static const unsigned int repair_deps[XFS_SCRUB_TYPE_NR] = {
	[XFS_SCRUB_TYPE_BMBTD]		= DEP(XFS_SCRUB_TYPE_INODE),
	[XFS_SCRUB_TYPE_BMBTA]		= DEP(XFS_SCRUB_TYPE_INODE),
	[XFS_SCRUB_TYPE_BMBTC]		= DEP(XFS_SCRUB_TYPE_INODE),
	[XFS_SCRUB_TYPE_DIR]		= DEP(XFS_SCRUB_TYPE_BMBTD),
	[XFS_SCRUB_TYPE_XATTR]		= DEP(XFS_SCRUB_TYPE_BMBTA),
	[XFS_SCRUB_TYPE_SYMLINK]	= DEP(XFS_SCRUB_TYPE_BMBTD),
	[XFS_SCRUB_TYPE_PARENT]		= DEP(XFS_SCRUB_TYPE_DIR) |
					  DEP(XFS_SCRUB_TYPE_XATTR),
	[XFS_SCRUB_TYPE_QUOTACHECK]	= DEP(XFS_SCRUB_TYPE_UQUOTA) |
					  DEP(XFS_SCRUB_TYPE_GQUOTA) |
					  DEP(XFS_SCRUB_TYPE_PQUOTA),
	[XFS_SCRUB_TYPE_RTSUM]		= DEP(XFS_SCRUB_TYPE_RTBITMAP),
};
#undef DEP

/*
 * Decide if we want an automatic downgrade to dry-run mode.  This is only
 * for service mode, where we are fed a path and have to figure out if the fs
 * is repairable or not.
 */
bool
repair_want_service_downgrade(
	struct scrub_ctx		*ctx)
{
	struct xfs_scrub_metadata	meta = {
		.sm_type		= XFS_SCRUB_TYPE_PROBE,
		.sm_flags		= XFS_SCRUB_IFLAG_REPAIR,
	};
	int				error;

	if (ctx->mode == SCRUB_MODE_DRY_RUN)
		return false;
	if (!is_service)
		return false;
	if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
		return false;

	error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
	switch (error) {
	case EROFS:
	case ENOTRECOVERABLE:
	case EOPNOTSUPP:
		return true;
	}

	return false;
}

static inline void
restore_oldvec(
	struct xfs_scrub_vec	*oldvec,
	const struct scrub_item	*sri,
	unsigned int		scrub_type)
{
	oldvec->sv_type = scrub_type;
	oldvec->sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
}

static int
repair_epilogue(
	struct scrub_ctx		*ctx,
	struct descr			*dsc,
	struct scrub_item		*sri,
	unsigned int			repair_flags,
	const struct xfs_scrub_vec	*meta)
{
	struct xfs_scrub_vec		oldv;
	struct xfs_scrub_vec		*oldm = &oldv;
	unsigned int			scrub_type = meta->sv_type;
	int				error = -meta->sv_ret;

	restore_oldvec(oldm, sri, meta->sv_type);

	switch (error) {
	case 0:
		/* No operational errors encountered. */
		break;
	case EDEADLOCK:
	case EBUSY:
		/* Filesystem is busy, try again later. */
		if (debug || verbose)
			str_info(ctx, descr_render(dsc),
_("Filesystem is busy, deferring repair."));
		return 0;
	case ESHUTDOWN:
		/* Filesystem is already shut down, abort. */
		str_error(ctx, descr_render(dsc),
_("Filesystem is shut down, aborting."));
		return ECANCELED;
	case ENOTTY:
	case EOPNOTSUPP:
		/*
		 * If the kernel cannot perform the optimization that we
		 * requested; or we forced a repair but the kernel doesn't know
		 * how to perform the repair, don't requeue the request.  Mark
		 * it done and move on.
		 */
		if (is_unoptimized(oldm) ||
		    debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
			scrub_item_clean_state(sri, scrub_type);
			return 0;
		}
		/*
		 * If we're in no-complain mode, requeue the check for
		 * later.  It's possible that an error in another
		 * component caused us to flag an error in this
		 * component.  Even if the kernel didn't think it
		 * could fix this, it's at least worth trying the scan
		 * again to see if another repair fixed it.
		 */
		if (!(repair_flags & XRM_FINAL_WARNING))
			return 0;
		fallthrough;
	case EINVAL:
		/* Kernel doesn't know how to repair this? */
		str_corrupt(ctx, descr_render(dsc),
_("Don't know how to fix; offline repair required."));
		scrub_item_clean_state(sri, scrub_type);
		return 0;
	case EROFS:
		/* Read-only filesystem, can't fix. */
		if (verbose || debug || needs_repair(oldm))
			str_error(ctx, descr_render(dsc),
_("Read-only filesystem; cannot make changes."));
		return ECANCELED;
	case ENOENT:
		/* Metadata not present, just skip it. */
		scrub_item_clean_state(sri, scrub_type);
		return 0;
	case ENOMEM:
	case ENOSPC:
		/* Don't care if preen fails due to low resources. */
		if (is_unoptimized(oldm) && !needs_repair(oldm)) {
			scrub_item_clean_state(sri, scrub_type);
			return 0;
		}
		fallthrough;
	default:
		/*
		 * Operational error.  If the caller doesn't want us to
		 * complain about repair failures, tell the caller to requeue
		 * the repair for later and don't say a thing.  Otherwise,
		 * print an error, mark the item clean because we're done with
		 * trying to repair it, and bail out.
		 */
		if (!(repair_flags & XRM_FINAL_WARNING))
			return 0;
		str_liberror(ctx, error, descr_render(dsc));
		scrub_item_clean_state(sri, scrub_type);
		return 0;
	}

	/*
	 * If the kernel says the repair was incomplete or that there was a
	 * cross-referencing discrepancy but no obvious corruption, we'll try
	 * the repair again, just in case the fs was busy.  Only retry so many
	 * times.
	 */
	if (want_retry(meta) && scrub_item_schedule_retry(sri, scrub_type))
		return 0;

	if (repair_flags & XRM_FINAL_WARNING)
		scrub_warn_incomplete_scrub(ctx, dsc, meta);
	if (needs_repair(meta) || is_incomplete(meta)) {
		/*
		 * Still broken; if we've been told not to complain then we
		 * just requeue this and try again later.  Otherwise we
		 * log the error loudly and don't try again.
		 */
		if (!(repair_flags & XRM_FINAL_WARNING))
			return 0;
		str_corrupt(ctx, descr_render(dsc),
_("Repair unsuccessful; offline repair required."));
	} else if (xref_failed(meta)) {
		/*
		 * This metadata object itself looks ok, but we still noticed
		 * inconsistencies when comparing it with the other filesystem
		 * metadata.  If we're in "final warning" mode, advise the
		 * caller to run xfs_repair; otherwise, we'll keep trying to
		 * reverify the cross-referencing as repairs progress.
		 */
		if (repair_flags & XRM_FINAL_WARNING) {
			str_info(ctx, descr_render(dsc),
 _("Seems correct but cross-referencing failed; offline repair recommended."));
		} else {
			if (verbose)
				str_info(ctx, descr_render(dsc),
 _("Seems correct but cross-referencing failed; will keep checking."));
			return 0;
		}
	} else if (meta->sv_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
		if (verbose)
			str_info(ctx, descr_render(dsc),
					_("No modification needed."));
	} else {
		/* Clean operation, no corruption detected. */
		if (is_corrupt(oldm))
			record_repair(ctx, descr_render(dsc),
 _("Repairs successful."));
		else if (xref_disagrees(oldm))
			record_repair(ctx, descr_render(dsc),
 _("Repairs successful after discrepancy in cross-referencing."));
		else if (xref_failed(oldm))
			record_repair(ctx, descr_render(dsc),
 _("Repairs successful after cross-referencing failure."));
		else
			record_preen(ctx, descr_render(dsc),
 _("Optimization successful."));
	}

	scrub_item_clean_state(sri, scrub_type);
	return 0;
}

/* Decide if the dependent scrub types of the given scrub type are ok. */
static bool
repair_item_dependencies_ok(
	const struct scrub_item	*sri,
	unsigned int		scrub_type)
{
	unsigned int		dep_mask = repair_deps[scrub_type];
	unsigned int		b;

	for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
		if (!(dep_mask & 1))
			continue;
		/*
		 * If this lower level object also needs repair, we can't fix
		 * the higher level item.
		 */
		if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
			return false;
	}

	return true;
}

/* Decide if we want to repair a particular type of metadata. */
static bool
can_repair_now(
	const struct scrub_item	*sri,
	unsigned int		scrub_type,
	__u32			repair_mask,
	unsigned int		repair_flags)
{
	struct xfs_scrub_vec	oldvec;
	bool			repair_only;

	/* Do we even need to repair this thing? */
	if (!(sri->sri_state[scrub_type] & repair_mask))
		return false;

	restore_oldvec(&oldvec, sri, scrub_type);

	/*
	 * If the caller boosted the priority of this scrub type on behalf of a
	 * higher level repair by setting IFLAG_REPAIR, ignore REPAIR_ONLY.
	 */
	repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
		      !(sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR);
	if (!is_corrupt(&oldvec) && repair_only)
		return false;

	/*
	 * Don't try to repair higher level items if their lower-level
	 * dependencies haven't been verified, unless this is our last chance
	 * to fix things without complaint.
	 */
	if (!(repair_flags & XRM_FINAL_WARNING) &&
	    !repair_item_dependencies_ok(sri, scrub_type))
		return false;

	return true;
}

/*
 * Repair some metadata.
 *
 * Returns 0 for success (or repair item deferral), or ECANCELED to abort the
 * program.
 */
static int
repair_call_kernel(
	struct scrub_ctx		*ctx,
	struct xfs_fd			*xfdp,
	struct scrub_item		*sri,
	__u32				repair_mask,
	unsigned int			repair_flags)
{
	DEFINE_DESCR(dsc, ctx, format_scrubv_descr);
	struct xfrog_scrubv		scrubv = { };
	struct scrubv_descr		vdesc = SCRUBV_DESCR(&scrubv);
	struct xfs_scrub_vec		*v;
	unsigned int			scrub_type;
	bool				need_barrier = false;
	int				error;

	assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));

	xfrog_scrubv_from_item(&scrubv, sri);
	descr_set(&dsc, &vdesc);

	foreach_scrub_type(scrub_type) {
		if (scrub_excessive_errors(ctx))
			return ECANCELED;

		if (!can_repair_now(sri, scrub_type, repair_mask,
					repair_flags))
			continue;

		if (need_barrier) {
			xfrog_scrubv_add_barrier(&scrubv);
			need_barrier = false;
		}

		xfrog_scrubv_add_item(&scrubv, sri, scrub_type, true);

		if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR)
			str_info(ctx, descr_render(&dsc),
					_("Attempting repair."));
		else if (debug || verbose)
			str_info(ctx, descr_render(&dsc),
					_("Attempting optimization."));

		dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc),
				sri->sri_state[scrub_type],
				sri->sri_tries[scrub_type]);

		/*
		 * One of the other scrub types depends on this one.  Set us up
		 * to add a repair barrier if we decide to schedule a repair
		 * after this one.  If the UNFIXED flag is set, that means this
		 * is our last chance to fix things, so we skip the barriers
		 * just let everything run.
		 */
		if (!(repair_flags & XRM_FINAL_WARNING) &&
		    (sri->sri_state[scrub_type] & SCRUB_ITEM_BARRIER))
			need_barrier = true;
	}

	error = -xfrog_scrubv_metadata(xfdp, &scrubv);
	if (error)
		return error;

	foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
		/* Deal with barriers separately. */
		if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) {
			/* -ECANCELED means the kernel stopped here. */
			if (v->sv_ret == -ECANCELED)
				return 0;
			if (v->sv_ret)
				return -v->sv_ret;
			continue;
		}

		error = repair_epilogue(ctx, &dsc, sri, repair_flags, v);
		if (error)
			return error;

		/* Maybe update progress if we fixed the problem. */
		if (!(repair_flags & XRM_NOPROGRESS) &&
		    !(sri->sri_state[v->sv_type] & SCRUB_ITEM_REPAIR_ANY))
			progress_add(1);
	}

	return 0;
}

/*
 * Prioritize action items in order of how long we can wait.
 *
 * To minimize the amount of repair work, we want to prioritize metadata
 * objects by perceived corruptness.  If CORRUPT is set, the fields are
 * just plain bad; try fixing that first.  Otherwise if XCORRUPT is set,
 * the fields could be bad, but the xref data could also be bad; we'll
 * try fixing that next.  Finally, if XFAIL is set, some other metadata
 * structure failed validation during xref, so we'll recheck this
 * metadata last since it was probably fine.
 *
 * For metadata that lie in the critical path of checking other metadata
 * (superblock, AG{F,I,FL}, inobt) we scrub and fix those things before
 * we even get to handling their dependencies, so things should progress
 * in order.
 */

struct action_item {
	struct list_head	list;
	struct scrub_item	sri;
};

/*
 * The operation of higher level metadata objects depends on the correctness of
 * lower level metadata objects.  This means that if X depends on Y, we must
 * investigate and correct all the observed issues with Y before we try to make
 * a correction to X.  For all scheduled repair activity on X, boost the
 * priority of repairs on all the Ys to ensure this correctness.
 */
static void
repair_item_boost_priorities(
	struct scrub_item		*sri)
{
	unsigned int			scrub_type;

	foreach_scrub_type(scrub_type) {
		unsigned int		dep_mask = repair_deps[scrub_type];
		unsigned int		b;

		if (repair_item_count_needsrepair(sri) == 0 || !dep_mask)
			continue;

		/*
		 * Check if the repairs for this scrub type depend on any other
		 * scrub types that have been flagged with cross-referencing
		 * errors and are not already tagged for the highest priority
		 * repair (SCRUB_ITEM_CORRUPT).  If so, boost the priority of
		 * that scrub type (via SCRUB_ITEM_BOOST_REPAIR) so that any
		 * problems with the dependencies will (hopefully) be fixed
		 * before we start repairs on this scrub type.
		 *
		 * So far in the history of xfs_scrub we have maintained that
		 * lower numbered scrub types do not depend on higher numbered
		 * scrub types, so we need only process the bit mask once.
		 */
		for (b = 0; b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
			if (!dep_mask)
				break;
			if (!(dep_mask & 1))
				continue;
			if (!(sri->sri_state[b] & SCRUB_ITEM_REPAIR_XREF))
				continue;
			if (sri->sri_state[b] & SCRUB_ITEM_CORRUPT)
				continue;
			sri->sri_state[b] |= SCRUB_ITEM_BOOST_REPAIR;
		}
	}
}

/*
 * These are the scrub item state bits that must be copied when scheduling
 * a (per-AG) scrub type for immediate repairs.  The original state tracking
 * bits are left untouched to force a rescan in phase 4.
 */
#define MUSTFIX_STATES	(SCRUB_ITEM_CORRUPT | \
			 SCRUB_ITEM_BOOST_REPAIR | \
			 SCRUB_ITEM_BARRIER)
/*
 * Figure out which AG metadata must be fixed before we can move on
 * to the inode scan.
 */
void
repair_item_mustfix(
	struct scrub_item	*sri,
	struct scrub_item	*fix_now)
{
	unsigned int		scrub_type;

	assert(sri->sri_agno != -1U);
	repair_item_boost_priorities(sri);
	scrub_item_init_ag(fix_now, sri->sri_agno);

	foreach_scrub_type(scrub_type) {
		unsigned int	state;

		state = sri->sri_state[scrub_type] & MUSTFIX_STATES;
		if (!state)
			continue;

		switch (scrub_type) {
		case XFS_SCRUB_TYPE_AGI:
		case XFS_SCRUB_TYPE_FINOBT:
		case XFS_SCRUB_TYPE_INOBT:
			fix_now->sri_state[scrub_type] = state;
			break;
		}
	}
}

/*
 * These scrub item states correspond to metadata that is inconsistent in some
 * way and must be repaired.  If too many metadata objects share these states,
 * this can make repairs difficult.
 */
#define HARDREPAIR_STATES	(SCRUB_ITEM_CORRUPT | \
				 SCRUB_ITEM_XCORRUPT | \
				 SCRUB_ITEM_XFAIL)

/* Determine if primary or secondary metadata are inconsistent. */
unsigned int
repair_item_difficulty(
	const struct scrub_item	*sri)
{
	unsigned int		scrub_type;
	unsigned int		ret = 0;

	foreach_scrub_type(scrub_type) {
		unsigned int	state;

		state = sri->sri_state[scrub_type] & HARDREPAIR_STATES;
		if (!state)
			continue;

		switch (scrub_type) {
		case XFS_SCRUB_TYPE_RMAPBT:
		case XFS_SCRUB_TYPE_RTRMAPBT:
			ret |= REPAIR_DIFFICULTY_SECONDARY;
			break;
		case XFS_SCRUB_TYPE_SB:
		case XFS_SCRUB_TYPE_AGF:
		case XFS_SCRUB_TYPE_AGFL:
		case XFS_SCRUB_TYPE_AGI:
		case XFS_SCRUB_TYPE_FINOBT:
		case XFS_SCRUB_TYPE_INOBT:
		case XFS_SCRUB_TYPE_BNOBT:
		case XFS_SCRUB_TYPE_CNTBT:
		case XFS_SCRUB_TYPE_REFCNTBT:
		case XFS_SCRUB_TYPE_RTBITMAP:
		case XFS_SCRUB_TYPE_RTSUM:
		case XFS_SCRUB_TYPE_RGSUPER:
		case XFS_SCRUB_TYPE_RTREFCBT:
			ret |= REPAIR_DIFFICULTY_PRIMARY;
			break;
		}
	}

	return ret;
}

/* Create a new repair action list. */
int
action_list_alloc(
	struct action_list		**listp)
{
	struct action_list		*alist;

	alist = malloc(sizeof(struct action_list));
	if (!alist)
		return errno;

	action_list_init(alist);
	*listp = alist;
	return 0;
}

/* Free the repair lists. */
void
action_list_free(
	struct action_list		**listp)
{
	struct action_list		*alist = *listp;
	struct action_item		*aitem;
	struct action_item		*n;

	if (!(*listp))
		return;

	list_for_each_entry_safe(aitem, n, &alist->list, list) {
		list_del(&aitem->list);
		free(aitem);
	}

	free(alist);
	*listp = NULL;
}

/* Number of pending repairs in this list. */
unsigned long long
action_list_length(
	struct action_list		*alist)
{
	struct action_item		*aitem;
	unsigned long long		ret = 0;

	list_for_each_entry(aitem, &alist->list, list)
		ret += repair_item_count_needsrepair(&aitem->sri);

	return ret;
}

/* Remove the first action item from the action list. */
struct action_item *
action_list_pop(
	struct action_list		*alist)
{
	struct action_item		*aitem;

	aitem = list_first_entry_or_null(&alist->list, struct action_item,
			list);
	if (!aitem)
		return NULL;

	list_del_init(&aitem->list);
	return aitem;
}

/* Add an action item to the end of a list. */
void
action_list_add(
	struct action_list		*alist,
	struct action_item		*aitem)
{
	list_add_tail(&aitem->list, &alist->list);
}

/*
 * Try to repair a filesystem object and let the caller know what it should do
 * with the action item.  The caller must be able to requeue action items, so
 * we don't complain if repairs are not totally successful.
 */
int
action_item_try_repair(
	struct scrub_ctx	*ctx,
	struct action_item	*aitem,
	enum tryrepair_outcome	*outcome)
{
	struct scrub_item	*sri = &aitem->sri;
	unsigned int		before, after;
	unsigned int		scrub_type;
	int			ret;

	BUILD_BUG_ON(sizeof(sri->sri_selected) * NBBY < XFS_SCRUB_TYPE_NR);
	before = repair_item_count_needsrepair(sri);

	ret = repair_item(ctx, sri, 0);
	if (ret)
		return ret;

	after = repair_item_count_needsrepair(sri);
	if (after > 0) {
		/*
		 * The kernel did not complete all of the repairs requested.
		 * If it made some progress we'll requeue; otherwise, let the
		 * caller know that nothing got fixed.
		 */
		if (before != after)
			*outcome = TR_REQUEUE;
		else
			*outcome = TR_NOPROGRESS;
		return 0;
	}

	/*
	 * Nothing in this fs object was marked inconsistent.  This means we
	 * were merely optimizing metadata and there is no revalidation work to
	 * be done.
	 */
	if (!sri->sri_inconsistent) {
		*outcome = TR_REPAIRED;
		return 0;
	}

	/*
	 * We fixed inconsistent metadata, so reschedule the entire object for
	 * immediate revalidation to see if anything else went wrong.
	 */
	foreach_scrub_type(scrub_type)
		if (sri->sri_selected & (1ULL << scrub_type))
			sri->sri_state[scrub_type] = SCRUB_ITEM_NEEDSCHECK;
	sri->sri_inconsistent = false;
	sri->sri_revalidate = true;

	ret = scrub_item_check(ctx, sri);
	if (ret)
		return ret;

	after = repair_item_count_needsrepair(sri);
	if (after > 0) {
		/*
		 * Uhoh, we found something else broken.  Tell the caller that
		 * this item needs to be queued for more repairs.
		 */
		sri->sri_revalidate = false;
		*outcome = TR_REQUEUE;
		return 0;
	}

	/* Repairs complete. */
	*outcome = TR_REPAIRED;
	return 0;
}

/* Repair everything on this list. */
int
action_list_process(
	struct scrub_ctx		*ctx,
	struct action_list		*alist,
	unsigned int			repair_flags)
{
	struct action_item		*aitem;
	struct action_item		*n;
	int				ret;

	list_for_each_entry_safe(aitem, n, &alist->list, list) {
		if (scrub_excessive_errors(ctx))
			return ECANCELED;

		ret = repair_item(ctx, &aitem->sri, repair_flags);
		if (ret)
			break;

		if (repair_item_count_needsrepair(&aitem->sri) == 0) {
			list_del(&aitem->list);
			free(aitem);
		}
	}

	return ret;
}

/*
 * For a given filesystem object, perform all repairs of a given class
 * (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed.
 */
static int
repair_item_class(
	struct scrub_ctx		*ctx,
	struct scrub_item		*sri,
	int				override_fd,
	uint8_t				repair_mask,
	unsigned int			flags)
{
	struct xfs_fd			xfd;
	struct scrub_item		old_sri;
	struct xfs_fd			*xfdp = &ctx->mnt;
	int				error = 0;

	if (ctx->mode == SCRUB_MODE_DRY_RUN)
		return 0;
	if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN))
		return 0;
	if (!scrub_item_schedule_work(sri, repair_mask, repair_deps))
		return 0;

	/*
	 * If the caller passed us a file descriptor for a scrub, use it
	 * instead of scrub-by-handle because this enables the kernel to skip
	 * costly inode btree lookups.
	 */
	if (override_fd >= 0) {
		memcpy(&xfd, xfdp, sizeof(xfd));
		xfd.fd = override_fd;
		xfdp = &xfd;
	}

	do {
		memcpy(&old_sri, sri, sizeof(struct scrub_item));
		error = repair_call_kernel(ctx, xfdp, sri, repair_mask, flags);
		if (error)
			return error;
	} while (scrub_item_call_kernel_again(sri, repair_mask, &old_sri));

	return 0;
}

/*
 * Repair all parts (i.e. scrub types) of this filesystem object for which
 * corruption has been observed directly.  Other types of repair work (fixing
 * cross referencing problems and preening) are deferred.
 *
 * This function should only be called to perform spot repairs of fs objects
 * during phase 2 and 3 while we still have open handles to those objects.
 */
int
repair_item_corruption(
	struct scrub_ctx	*ctx,
	struct scrub_item	*sri)
{
	return repair_item_class(ctx, sri, -1, SCRUB_ITEM_CORRUPT,
			XRM_REPAIR_ONLY | XRM_NOPROGRESS);
}

/* Repair all parts of this file, similar to repair_item_corruption. */
int
repair_file_corruption(
	struct scrub_ctx	*ctx,
	struct scrub_item	*sri,
	int			override_fd)
{
	repair_item_boost_priorities(sri);

	return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT,
			XRM_REPAIR_ONLY | XRM_NOPROGRESS);
}

/* Repair all parts of this file or complain if we cannot. */
int
repair_file_corruption_now(
	struct scrub_ctx	*ctx,
	struct scrub_item	*sri,
	int			override_fd)
{
	repair_item_boost_priorities(sri);

	return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT,
			XRM_REPAIR_ONLY | XRM_NOPROGRESS | XRM_FINAL_WARNING);
}

/*
 * Repair everything in this filesystem object that needs it.  This includes
 * cross-referencing and preening.
 */
int
repair_item(
	struct scrub_ctx	*ctx,
	struct scrub_item	*sri,
	unsigned int		flags)
{
	int			ret;

	repair_item_boost_priorities(sri);

	ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_CORRUPT, flags);
	if (ret)
		return ret;

	ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_XCORRUPT, flags);
	if (ret)
		return ret;

	ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_XFAIL, flags);
	if (ret)
		return ret;

	return repair_item_class(ctx, sri, -1, SCRUB_ITEM_PREEN, flags);
}

/* Create an action item around a scrub item that needs repairs. */
int
repair_item_to_action_item(
	struct scrub_ctx	*ctx,
	const struct scrub_item	*sri,
	struct action_item	**aitemp)
{
	struct action_item	*aitem;
	unsigned int		scrub_type;

	if (repair_item_count_needsrepair(sri) == 0)
		return 0;

	aitem = malloc(sizeof(struct action_item));
	if (!aitem) {
		int		error = errno;

		str_liberror(ctx, error, _("creating repair action item"));
		return error;
	}

	INIT_LIST_HEAD(&aitem->list);
	memcpy(&aitem->sri, sri, sizeof(struct scrub_item));

	/*
	 * If the scrub item indicates that there is unchecked metadata, assume
	 * that the scrub type checker depends on something that couldn't be
	 * fixed.  Mark that type as corrupt so that phase 4 will try it again.
	 */
	foreach_scrub_type(scrub_type) {
		__u8		*state = aitem->sri.sri_state;

		if (state[scrub_type] & SCRUB_ITEM_NEEDSCHECK) {
			state[scrub_type] &= ~SCRUB_ITEM_NEEDSCHECK;
			state[scrub_type] |= SCRUB_ITEM_CORRUPT;
		}
	}

	*aitemp = aitem;
	return 0;
}