1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
11 #include <sys/types.h>
12 #include <sys/statvfs.h>
14 #include "libfrog/paths.h"
15 #include "libfrog/fsgeom.h"
16 #include "libfrog/scrub.h"
17 #include "xfs_scrub.h"
24 /* Online scrub and repair wrappers. */
26 /* Format a scrub description. */
29 struct scrub_ctx
*ctx
,
34 struct xfs_scrub_metadata
*meta
= where
;
35 const struct xfrog_scrub_descr
*sc
= &xfrog_scrubbers
[meta
->sm_type
];
38 case XFROG_SCRUB_TYPE_AGHEADER
:
39 case XFROG_SCRUB_TYPE_PERAG
:
40 return snprintf(buf
, buflen
, _("AG %u %s"), meta
->sm_agno
,
43 case XFROG_SCRUB_TYPE_INODE
:
44 return scrub_render_ino_descr(ctx
, buf
, buflen
,
45 meta
->sm_ino
, meta
->sm_gen
, "%s",
48 case XFROG_SCRUB_TYPE_FS
:
49 return snprintf(buf
, buflen
, _("%s"), _(sc
->descr
));
51 case XFROG_SCRUB_TYPE_NONE
:
58 /* Predicates for scrub flag state. */
60 static inline bool is_corrupt(struct xfs_scrub_metadata
*sm
)
62 return sm
->sm_flags
& XFS_SCRUB_OFLAG_CORRUPT
;
65 static inline bool is_unoptimized(struct xfs_scrub_metadata
*sm
)
67 return sm
->sm_flags
& XFS_SCRUB_OFLAG_PREEN
;
70 static inline bool xref_failed(struct xfs_scrub_metadata
*sm
)
72 return sm
->sm_flags
& XFS_SCRUB_OFLAG_XFAIL
;
75 static inline bool xref_disagrees(struct xfs_scrub_metadata
*sm
)
77 return sm
->sm_flags
& XFS_SCRUB_OFLAG_XCORRUPT
;
80 static inline bool is_incomplete(struct xfs_scrub_metadata
*sm
)
82 return sm
->sm_flags
& XFS_SCRUB_OFLAG_INCOMPLETE
;
85 static inline bool is_suspicious(struct xfs_scrub_metadata
*sm
)
87 return sm
->sm_flags
& XFS_SCRUB_OFLAG_WARNING
;
90 /* Should we fix it? */
91 static inline bool needs_repair(struct xfs_scrub_metadata
*sm
)
93 return is_corrupt(sm
) || xref_disagrees(sm
);
96 /* Warn about strange circumstances after scrub. */
98 scrub_warn_incomplete_scrub(
99 struct scrub_ctx
*ctx
,
101 struct xfs_scrub_metadata
*meta
)
103 if (is_incomplete(meta
))
104 str_info(ctx
, descr_render(dsc
), _("Check incomplete."));
106 if (is_suspicious(meta
)) {
108 str_info(ctx
, descr_render(dsc
),
109 _("Possibly suspect metadata."));
111 str_warn(ctx
, descr_render(dsc
),
112 _("Possibly suspect metadata."));
115 if (xref_failed(meta
))
116 str_info(ctx
, descr_render(dsc
),
117 _("Cross-referencing failed."));
120 /* Do a read-only check of some metadata. */
121 static enum check_outcome
123 struct scrub_ctx
*ctx
,
125 struct xfs_scrub_metadata
*meta
,
128 DEFINE_DESCR(dsc
, ctx
, format_scrub_descr
);
129 unsigned int tries
= 0;
132 assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
133 assert(meta
->sm_type
< XFS_SCRUB_TYPE_NR
);
134 descr_set(&dsc
, meta
);
136 dbg_printf("check %s flags %xh\n", descr_render(&dsc
), meta
->sm_flags
);
138 error
= -xfrog_scrub_metadata(xfdp
, meta
);
139 if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error
)
140 meta
->sm_flags
|= XFS_SCRUB_OFLAG_CORRUPT
;
143 /* No operational errors encountered. */
146 /* Metadata not present, just skip it. */
149 /* FS already crashed, give up. */
150 str_error(ctx
, descr_render(&dsc
),
151 _("Filesystem is shut down, aborting."));
155 /* Abort on I/O errors or insufficient memory. */
156 str_liberror(ctx
, error
, descr_render(&dsc
));
163 * The first two should never escape the kernel,
164 * and the other two should be reported via sm_flags.
166 str_liberror(ctx
, error
, _("Kernel bug"));
169 /* Operational error. */
170 str_liberror(ctx
, error
, descr_render(&dsc
));
175 * If the kernel says the test was incomplete or that there was
176 * a cross-referencing discrepancy but no obvious corruption,
177 * we'll try the scan again, just in case the fs was busy.
178 * Only retry so many times.
180 if (tries
< 10 && (is_incomplete(meta
) ||
181 (xref_disagrees(meta
) && !is_corrupt(meta
)))) {
186 /* Complain about incomplete or suspicious metadata. */
187 scrub_warn_incomplete_scrub(ctx
, &dsc
, meta
);
190 * If we need repairs or there were discrepancies, schedule a
191 * repair if desired, otherwise complain.
193 if (is_corrupt(meta
) || xref_disagrees(meta
)) {
194 if (ctx
->mode
< SCRUB_MODE_REPAIR
) {
195 str_corrupt(ctx
, descr_render(&dsc
),
196 _("Repairs are required."));
204 * If we could optimize, schedule a repair if desired,
205 * otherwise complain.
207 if (is_unoptimized(meta
)) {
208 if (ctx
->mode
!= SCRUB_MODE_REPAIR
) {
210 /* AG or FS metadata, always warn. */
211 str_info(ctx
, descr_render(&dsc
),
212 _("Optimization is possible."));
213 } else if (!ctx
->preen_triggers
[meta
->sm_type
]) {
214 /* File metadata, only warn once per type. */
215 pthread_mutex_lock(&ctx
->lock
);
216 if (!ctx
->preen_triggers
[meta
->sm_type
])
217 ctx
->preen_triggers
[meta
->sm_type
] = true;
218 pthread_mutex_unlock(&ctx
->lock
);
227 * This metadata object itself looks ok, but we noticed inconsistencies
228 * when comparing it with the other filesystem metadata. If we're in
229 * repair mode we need to queue it for a "repair" so that phase 4 will
230 * re-examine the object as repairs progress to see if the kernel will
231 * deem it completely consistent at some point.
233 if (xref_failed(meta
) && ctx
->mode
== SCRUB_MODE_REPAIR
)
236 /* Everything is ok. */
240 /* Bulk-notify user about things that could be optimized. */
242 scrub_report_preen_triggers(
243 struct scrub_ctx
*ctx
)
247 for (i
= 0; i
< XFS_SCRUB_TYPE_NR
; i
++) {
248 pthread_mutex_lock(&ctx
->lock
);
249 if (ctx
->preen_triggers
[i
]) {
250 ctx
->preen_triggers
[i
] = false;
251 pthread_mutex_unlock(&ctx
->lock
);
252 str_info(ctx
, ctx
->mntpoint
,
253 _("Optimizations of %s are possible."), _(xfrog_scrubbers
[i
].descr
));
255 pthread_mutex_unlock(&ctx
->lock
);
260 /* Save a scrub context for later repairs. */
263 struct scrub_ctx
*ctx
,
264 struct action_list
*alist
,
265 struct xfs_scrub_metadata
*meta
)
267 struct action_item
*aitem
;
269 /* Schedule this item for later repairs. */
270 aitem
= malloc(sizeof(struct action_item
));
272 str_errno(ctx
, _("adding item to repair list"));
276 memset(aitem
, 0, sizeof(*aitem
));
277 aitem
->type
= meta
->sm_type
;
278 aitem
->flags
= meta
->sm_flags
;
279 switch (xfrog_scrubbers
[meta
->sm_type
].type
) {
280 case XFROG_SCRUB_TYPE_AGHEADER
:
281 case XFROG_SCRUB_TYPE_PERAG
:
282 aitem
->agno
= meta
->sm_agno
;
284 case XFROG_SCRUB_TYPE_INODE
:
285 aitem
->ino
= meta
->sm_ino
;
286 aitem
->gen
= meta
->sm_gen
;
292 action_list_add(alist
, aitem
);
297 * Scrub a single XFS_SCRUB_TYPE_*, saving corruption reports for later.
299 * Returns 0 for success. If errors occur, this function will log them and
300 * return a positive error code.
304 struct scrub_ctx
*ctx
,
307 struct action_list
*alist
)
309 struct xfs_scrub_metadata meta
= {
313 enum check_outcome fix
;
318 /* Check the item. */
319 fix
= xfs_check_metadata(ctx
, &ctx
->mnt
, &meta
, false);
326 ret
= scrub_save_repair(ctx
, alist
, &meta
);
333 /* CHECK_RETRY should never happen. */
339 * Scrub all metadata types that are assigned to the given XFROG_SCRUB_TYPE_*,
340 * saving corruption reports for later. This should not be used for
341 * XFROG_SCRUB_TYPE_INODE or for checking summary metadata.
345 struct scrub_ctx
*ctx
,
346 enum xfrog_scrub_type scrub_type
,
348 struct action_list
*alist
)
350 const struct xfrog_scrub_descr
*sc
;
353 sc
= xfrog_scrubbers
;
354 for (type
= 0; type
< XFS_SCRUB_TYPE_NR
; type
++, sc
++) {
357 if (sc
->type
!= scrub_type
)
359 if (sc
->flags
& XFROG_SCRUB_DESCR_SUMMARY
)
362 ret
= scrub_meta_type(ctx
, type
, agno
, alist
);
371 * Scrub primary superblock. This will be useful if we ever need to hook
372 * a filesystem-wide pre-scrub activity off of the sb 0 scrubber (which
373 * currently does nothing). If errors occur, this function will log them and
378 struct scrub_ctx
*ctx
,
379 struct action_list
*alist
)
381 return scrub_meta_type(ctx
, XFS_SCRUB_TYPE_SB
, 0, alist
);
384 /* Scrub each AG's header blocks. */
387 struct scrub_ctx
*ctx
,
389 struct action_list
*alist
)
391 return scrub_all_types(ctx
, XFROG_SCRUB_TYPE_AGHEADER
, agno
, alist
);
394 /* Scrub each AG's metadata btrees. */
397 struct scrub_ctx
*ctx
,
399 struct action_list
*alist
)
401 return scrub_all_types(ctx
, XFROG_SCRUB_TYPE_PERAG
, agno
, alist
);
404 /* Scrub whole-FS metadata btrees. */
407 struct scrub_ctx
*ctx
,
408 struct action_list
*alist
)
410 return scrub_all_types(ctx
, XFROG_SCRUB_TYPE_FS
, 0, alist
);
413 /* Scrub FS summary metadata. */
416 struct scrub_ctx
*ctx
,
417 struct action_list
*alist
)
419 return scrub_meta_type(ctx
, XFS_SCRUB_TYPE_FSCOUNTERS
, 0, alist
);
422 /* How many items do we have to check? */
424 scrub_estimate_ag_work(
425 struct scrub_ctx
*ctx
)
427 const struct xfrog_scrub_descr
*sc
;
429 unsigned int estimate
= 0;
431 sc
= xfrog_scrubbers
;
432 for (type
= 0; type
< XFS_SCRUB_TYPE_NR
; type
++, sc
++) {
434 case XFROG_SCRUB_TYPE_AGHEADER
:
435 case XFROG_SCRUB_TYPE_PERAG
:
436 estimate
+= ctx
->mnt
.fsgeom
.agcount
;
438 case XFROG_SCRUB_TYPE_FS
:
449 * Scrub file metadata of some sort. If errors occur, this function will log
450 * them and return nonzero.
454 struct scrub_ctx
*ctx
,
456 const struct xfs_bulkstat
*bstat
,
458 struct action_list
*alist
)
460 struct xfs_scrub_metadata meta
= {0};
462 struct xfs_fd
*xfdp
= &ctx
->mnt
;
463 enum check_outcome fix
;
465 assert(type
< XFS_SCRUB_TYPE_NR
);
466 assert(xfrog_scrubbers
[type
].type
== XFROG_SCRUB_TYPE_INODE
);
469 meta
.sm_ino
= bstat
->bs_ino
;
470 meta
.sm_gen
= bstat
->bs_gen
;
473 * If the caller passed us a file descriptor for a scrub, use it
474 * instead of scrub-by-handle because this enables the kernel to skip
475 * costly inode btree lookups.
478 memcpy(&xfd
, xfdp
, sizeof(xfd
));
483 /* Scrub the piece of metadata. */
484 fix
= xfs_check_metadata(ctx
, xfdp
, &meta
, true);
485 if (fix
== CHECK_ABORT
)
487 if (fix
== CHECK_DONE
)
490 return scrub_save_repair(ctx
, alist
, &meta
);
494 * Test the availability of a kernel scrub command. If errors occur (or the
495 * scrub ioctl is rejected) the errors will be logged and this function will
500 struct scrub_ctx
*ctx
,
504 struct xfs_scrub_metadata meta
= {0};
507 if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
511 meta
.sm_flags
= flags
;
512 error
= -xfrog_scrub_metadata(&ctx
->mnt
, &meta
);
517 str_info(ctx
, ctx
->mntpoint
,
518 _("Filesystem is mounted read-only; cannot proceed."));
520 case ENOTRECOVERABLE
:
521 str_info(ctx
, ctx
->mntpoint
,
522 _("Filesystem is mounted norecovery; cannot proceed."));
527 if (debug
|| verbose
)
528 str_info(ctx
, ctx
->mntpoint
,
529 _("Kernel %s %s facility not detected."),
530 _(xfrog_scrubbers
[type
].descr
),
531 (flags
& XFS_SCRUB_IFLAG_REPAIR
) ?
532 _("repair") : _("scrub"));
535 /* Scrubber says not present on this fs; that's fine. */
538 str_info(ctx
, ctx
->mntpoint
, "%s", strerror(errno
));
544 can_scrub_fs_metadata(
545 struct scrub_ctx
*ctx
)
547 return __scrub_test(ctx
, XFS_SCRUB_TYPE_PROBE
, 0);
552 struct scrub_ctx
*ctx
)
554 return __scrub_test(ctx
, XFS_SCRUB_TYPE_INODE
, 0);
559 struct scrub_ctx
*ctx
)
561 return __scrub_test(ctx
, XFS_SCRUB_TYPE_BMBTD
, 0);
566 struct scrub_ctx
*ctx
)
568 return __scrub_test(ctx
, XFS_SCRUB_TYPE_DIR
, 0);
573 struct scrub_ctx
*ctx
)
575 return __scrub_test(ctx
, XFS_SCRUB_TYPE_XATTR
, 0);
580 struct scrub_ctx
*ctx
)
582 return __scrub_test(ctx
, XFS_SCRUB_TYPE_SYMLINK
, 0);
587 struct scrub_ctx
*ctx
)
589 return __scrub_test(ctx
, XFS_SCRUB_TYPE_PARENT
, 0);
594 struct scrub_ctx
*ctx
)
596 return __scrub_test(ctx
, XFS_SCRUB_TYPE_PROBE
, XFS_SCRUB_IFLAG_REPAIR
);
601 struct scrub_ctx
*ctx
)
603 return __scrub_test(ctx
, XFS_SCRUB_TYPE_PROBE
,
604 XFS_SCRUB_IFLAG_REPAIR
| XFS_SCRUB_IFLAG_FORCE_REBUILD
);
607 /* General repair routines. */
609 /* Repair some metadata. */
612 struct scrub_ctx
*ctx
,
614 struct action_item
*aitem
,
615 unsigned int repair_flags
)
617 struct xfs_scrub_metadata meta
= { 0 };
618 struct xfs_scrub_metadata oldm
;
619 DEFINE_DESCR(dsc
, ctx
, format_scrub_descr
);
622 assert(aitem
->type
< XFS_SCRUB_TYPE_NR
);
623 assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
624 meta
.sm_type
= aitem
->type
;
625 meta
.sm_flags
= aitem
->flags
| XFS_SCRUB_IFLAG_REPAIR
;
626 if (use_force_rebuild
)
627 meta
.sm_flags
|= XFS_SCRUB_IFLAG_FORCE_REBUILD
;
628 switch (xfrog_scrubbers
[aitem
->type
].type
) {
629 case XFROG_SCRUB_TYPE_AGHEADER
:
630 case XFROG_SCRUB_TYPE_PERAG
:
631 meta
.sm_agno
= aitem
->agno
;
633 case XFROG_SCRUB_TYPE_INODE
:
634 meta
.sm_ino
= aitem
->ino
;
635 meta
.sm_gen
= aitem
->gen
;
641 if (!is_corrupt(&meta
) && (repair_flags
& XRM_REPAIR_ONLY
))
644 memcpy(&oldm
, &meta
, sizeof(oldm
));
645 descr_set(&dsc
, &oldm
);
647 if (needs_repair(&meta
))
648 str_info(ctx
, descr_render(&dsc
), _("Attempting repair."));
649 else if (debug
|| verbose
)
650 str_info(ctx
, descr_render(&dsc
),
651 _("Attempting optimization."));
653 error
= -xfrog_scrub_metadata(xfdp
, &meta
);
656 /* No operational errors encountered. */
660 /* Filesystem is busy, try again later. */
661 if (debug
|| verbose
)
662 str_info(ctx
, descr_render(&dsc
),
663 _("Filesystem is busy, deferring repair."));
666 /* Filesystem is already shut down, abort. */
667 str_error(ctx
, descr_render(&dsc
),
668 _("Filesystem is shut down, aborting."));
673 * If the kernel cannot perform the optimization that we
674 * requested; or we forced a repair but the kernel doesn't know
675 * how to perform the repair, don't requeue the request. Mark
676 * it done and move on.
678 if (is_unoptimized(&oldm
) ||
679 debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
682 * If we're in no-complain mode, requeue the check for
683 * later. It's possible that an error in another
684 * component caused us to flag an error in this
685 * component. Even if the kernel didn't think it
686 * could fix this, it's at least worth trying the scan
687 * again to see if another repair fixed it.
689 if (!(repair_flags
& XRM_COMPLAIN_IF_UNFIXED
))
693 /* Kernel doesn't know how to repair this? */
694 str_corrupt(ctx
, descr_render(&dsc
),
695 _("Don't know how to fix; offline repair required."));
698 /* Read-only filesystem, can't fix. */
699 if (verbose
|| debug
|| needs_repair(&oldm
))
700 str_error(ctx
, descr_render(&dsc
),
701 _("Read-only filesystem; cannot make changes."));
704 /* Metadata not present, just skip it. */
708 /* Don't care if preen fails due to low resources. */
709 if (is_unoptimized(&oldm
) && !needs_repair(&oldm
))
714 * Operational error. If the caller doesn't want us
715 * to complain about repair failures, tell the caller
716 * to requeue the repair for later and don't say a
717 * thing. Otherwise, print error and bail out.
719 if (!(repair_flags
& XRM_COMPLAIN_IF_UNFIXED
))
721 str_liberror(ctx
, error
, descr_render(&dsc
));
725 if (repair_flags
& XRM_COMPLAIN_IF_UNFIXED
)
726 scrub_warn_incomplete_scrub(ctx
, &dsc
, &meta
);
727 if (needs_repair(&meta
)) {
729 * Still broken; if we've been told not to complain then we
730 * just requeue this and try again later. Otherwise we
731 * log the error loudly and don't try again.
733 if (!(repair_flags
& XRM_COMPLAIN_IF_UNFIXED
))
735 str_corrupt(ctx
, descr_render(&dsc
),
736 _("Repair unsuccessful; offline repair required."));
737 } else if (xref_failed(&meta
)) {
739 * This metadata object itself looks ok, but we still noticed
740 * inconsistencies when comparing it with the other filesystem
741 * metadata. If we're in "final warning" mode, advise the
742 * caller to run xfs_repair; otherwise, we'll keep trying to
743 * reverify the cross-referencing as repairs progress.
745 if (repair_flags
& XRM_COMPLAIN_IF_UNFIXED
) {
746 str_info(ctx
, descr_render(&dsc
),
747 _("Seems correct but cross-referencing failed; offline repair recommended."));
750 str_info(ctx
, descr_render(&dsc
),
751 _("Seems correct but cross-referencing failed; will keep checking."));
755 /* Clean operation, no corruption detected. */
756 if (needs_repair(&oldm
))
757 record_repair(ctx
, descr_render(&dsc
),
758 _("Repairs successful."));
760 record_preen(ctx
, descr_render(&dsc
),
761 _("Optimization successful."));