]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/scrub.c
xfs_scrub: update copyright years for scrub/ files
[thirdparty/xfsprogs-dev.git] / scrub / scrub.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
5 */
6 #include "xfs.h"
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/statvfs.h>
13 #include "list.h"
14 #include "libfrog/paths.h"
15 #include "libfrog/fsgeom.h"
16 #include "libfrog/scrub.h"
17 #include "xfs_scrub.h"
18 #include "common.h"
19 #include "progress.h"
20 #include "scrub.h"
21 #include "repair.h"
22 #include "descr.h"
23
24 /* Online scrub and repair wrappers. */
25
26 /* Format a scrub description. */
27 static int
28 format_scrub_descr(
29 struct scrub_ctx *ctx,
30 char *buf,
31 size_t buflen,
32 void *where)
33 {
34 struct xfs_scrub_metadata *meta = where;
35 const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[meta->sm_type];
36
37 switch (sc->type) {
38 case XFROG_SCRUB_TYPE_AGHEADER:
39 case XFROG_SCRUB_TYPE_PERAG:
40 return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
41 _(sc->descr));
42 break;
43 case XFROG_SCRUB_TYPE_INODE:
44 return scrub_render_ino_descr(ctx, buf, buflen,
45 meta->sm_ino, meta->sm_gen, "%s",
46 _(sc->descr));
47 break;
48 case XFROG_SCRUB_TYPE_FS:
49 return snprintf(buf, buflen, _("%s"), _(sc->descr));
50 break;
51 case XFROG_SCRUB_TYPE_NONE:
52 assert(0);
53 break;
54 }
55 return -1;
56 }
57
58 /* Predicates for scrub flag state. */
59
60 static inline bool is_corrupt(struct xfs_scrub_metadata *sm)
61 {
62 return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT;
63 }
64
65 static inline bool is_unoptimized(struct xfs_scrub_metadata *sm)
66 {
67 return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN;
68 }
69
70 static inline bool xref_failed(struct xfs_scrub_metadata *sm)
71 {
72 return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL;
73 }
74
75 static inline bool xref_disagrees(struct xfs_scrub_metadata *sm)
76 {
77 return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT;
78 }
79
80 static inline bool is_incomplete(struct xfs_scrub_metadata *sm)
81 {
82 return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
83 }
84
85 static inline bool is_suspicious(struct xfs_scrub_metadata *sm)
86 {
87 return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING;
88 }
89
90 /* Should we fix it? */
91 static inline bool needs_repair(struct xfs_scrub_metadata *sm)
92 {
93 return is_corrupt(sm) || xref_disagrees(sm);
94 }
95
96 /* Warn about strange circumstances after scrub. */
97 static inline void
98 scrub_warn_incomplete_scrub(
99 struct scrub_ctx *ctx,
100 struct descr *dsc,
101 struct xfs_scrub_metadata *meta)
102 {
103 if (is_incomplete(meta))
104 str_info(ctx, descr_render(dsc), _("Check incomplete."));
105
106 if (is_suspicious(meta)) {
107 if (debug)
108 str_info(ctx, descr_render(dsc),
109 _("Possibly suspect metadata."));
110 else
111 str_warn(ctx, descr_render(dsc),
112 _("Possibly suspect metadata."));
113 }
114
115 if (xref_failed(meta))
116 str_info(ctx, descr_render(dsc),
117 _("Cross-referencing failed."));
118 }
119
120 /* Do a read-only check of some metadata. */
121 static enum check_outcome
122 xfs_check_metadata(
123 struct scrub_ctx *ctx,
124 struct xfs_fd *xfdp,
125 struct xfs_scrub_metadata *meta,
126 bool is_inode)
127 {
128 DEFINE_DESCR(dsc, ctx, format_scrub_descr);
129 unsigned int tries = 0;
130 int error;
131
132 assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
133 assert(meta->sm_type < XFS_SCRUB_TYPE_NR);
134 descr_set(&dsc, meta);
135
136 dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta->sm_flags);
137 retry:
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;
141 switch (error) {
142 case 0:
143 /* No operational errors encountered. */
144 break;
145 case ENOENT:
146 /* Metadata not present, just skip it. */
147 return CHECK_DONE;
148 case ESHUTDOWN:
149 /* FS already crashed, give up. */
150 str_error(ctx, descr_render(&dsc),
151 _("Filesystem is shut down, aborting."));
152 return CHECK_ABORT;
153 case EIO:
154 case ENOMEM:
155 /* Abort on I/O errors or insufficient memory. */
156 str_liberror(ctx, error, descr_render(&dsc));
157 return CHECK_ABORT;
158 case EDEADLOCK:
159 case EBUSY:
160 case EFSBADCRC:
161 case EFSCORRUPTED:
162 /*
163 * The first two should never escape the kernel,
164 * and the other two should be reported via sm_flags.
165 */
166 str_liberror(ctx, error, _("Kernel bug"));
167 return CHECK_DONE;
168 default:
169 /* Operational error. */
170 str_liberror(ctx, error, descr_render(&dsc));
171 return CHECK_DONE;
172 }
173
174 /*
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.
179 */
180 if (tries < 10 && (is_incomplete(meta) ||
181 (xref_disagrees(meta) && !is_corrupt(meta)))) {
182 tries++;
183 goto retry;
184 }
185
186 /* Complain about incomplete or suspicious metadata. */
187 scrub_warn_incomplete_scrub(ctx, &dsc, meta);
188
189 /*
190 * If we need repairs or there were discrepancies, schedule a
191 * repair if desired, otherwise complain.
192 */
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."));
197 return CHECK_DONE;
198 }
199
200 return CHECK_REPAIR;
201 }
202
203 /*
204 * If we could optimize, schedule a repair if desired,
205 * otherwise complain.
206 */
207 if (is_unoptimized(meta)) {
208 if (ctx->mode != SCRUB_MODE_REPAIR) {
209 if (!is_inode) {
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);
219 }
220 return CHECK_DONE;
221 }
222
223 return CHECK_REPAIR;
224 }
225
226 /*
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.
232 */
233 if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR)
234 return CHECK_REPAIR;
235
236 /* Everything is ok. */
237 return CHECK_DONE;
238 }
239
240 /* Bulk-notify user about things that could be optimized. */
241 void
242 scrub_report_preen_triggers(
243 struct scrub_ctx *ctx)
244 {
245 int i;
246
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));
254 } else {
255 pthread_mutex_unlock(&ctx->lock);
256 }
257 }
258 }
259
260 /* Save a scrub context for later repairs. */
261 static int
262 scrub_save_repair(
263 struct scrub_ctx *ctx,
264 struct action_list *alist,
265 struct xfs_scrub_metadata *meta)
266 {
267 struct action_item *aitem;
268
269 /* Schedule this item for later repairs. */
270 aitem = malloc(sizeof(struct action_item));
271 if (!aitem) {
272 str_errno(ctx, _("adding item to repair list"));
273 return errno;
274 }
275
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;
283 break;
284 case XFROG_SCRUB_TYPE_INODE:
285 aitem->ino = meta->sm_ino;
286 aitem->gen = meta->sm_gen;
287 break;
288 default:
289 break;
290 }
291
292 action_list_add(alist, aitem);
293 return 0;
294 }
295
296 /*
297 * Scrub a single XFS_SCRUB_TYPE_*, saving corruption reports for later.
298 *
299 * Returns 0 for success. If errors occur, this function will log them and
300 * return a positive error code.
301 */
302 static int
303 scrub_meta_type(
304 struct scrub_ctx *ctx,
305 unsigned int type,
306 xfs_agnumber_t agno,
307 struct action_list *alist)
308 {
309 struct xfs_scrub_metadata meta = {
310 .sm_type = type,
311 .sm_agno = agno,
312 };
313 enum check_outcome fix;
314 int ret;
315
316 background_sleep();
317
318 /* Check the item. */
319 fix = xfs_check_metadata(ctx, &ctx->mnt, &meta, false);
320 progress_add(1);
321
322 switch (fix) {
323 case CHECK_ABORT:
324 return ECANCELED;
325 case CHECK_REPAIR:
326 ret = scrub_save_repair(ctx, alist, &meta);
327 if (ret)
328 return ret;
329 fallthrough;
330 case CHECK_DONE:
331 return 0;
332 default:
333 /* CHECK_RETRY should never happen. */
334 abort();
335 }
336 }
337
338 /*
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.
342 */
343 static bool
344 scrub_all_types(
345 struct scrub_ctx *ctx,
346 enum xfrog_scrub_type scrub_type,
347 xfs_agnumber_t agno,
348 struct action_list *alist)
349 {
350 const struct xfrog_scrub_descr *sc;
351 unsigned int type;
352
353 sc = xfrog_scrubbers;
354 for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
355 int ret;
356
357 if (sc->type != scrub_type)
358 continue;
359 if (sc->flags & XFROG_SCRUB_DESCR_SUMMARY)
360 continue;
361
362 ret = scrub_meta_type(ctx, type, agno, alist);
363 if (ret)
364 return ret;
365 }
366
367 return 0;
368 }
369
370 /*
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
374 * return nonzero.
375 */
376 int
377 scrub_primary_super(
378 struct scrub_ctx *ctx,
379 struct action_list *alist)
380 {
381 return scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, alist);
382 }
383
384 /* Scrub each AG's header blocks. */
385 int
386 scrub_ag_headers(
387 struct scrub_ctx *ctx,
388 xfs_agnumber_t agno,
389 struct action_list *alist)
390 {
391 return scrub_all_types(ctx, XFROG_SCRUB_TYPE_AGHEADER, agno, alist);
392 }
393
394 /* Scrub each AG's metadata btrees. */
395 int
396 scrub_ag_metadata(
397 struct scrub_ctx *ctx,
398 xfs_agnumber_t agno,
399 struct action_list *alist)
400 {
401 return scrub_all_types(ctx, XFROG_SCRUB_TYPE_PERAG, agno, alist);
402 }
403
404 /* Scrub whole-FS metadata btrees. */
405 int
406 scrub_fs_metadata(
407 struct scrub_ctx *ctx,
408 struct action_list *alist)
409 {
410 return scrub_all_types(ctx, XFROG_SCRUB_TYPE_FS, 0, alist);
411 }
412
413 /* Scrub FS summary metadata. */
414 int
415 scrub_fs_summary(
416 struct scrub_ctx *ctx,
417 struct action_list *alist)
418 {
419 return scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, alist);
420 }
421
422 /* How many items do we have to check? */
423 unsigned int
424 scrub_estimate_ag_work(
425 struct scrub_ctx *ctx)
426 {
427 const struct xfrog_scrub_descr *sc;
428 int type;
429 unsigned int estimate = 0;
430
431 sc = xfrog_scrubbers;
432 for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
433 switch (sc->type) {
434 case XFROG_SCRUB_TYPE_AGHEADER:
435 case XFROG_SCRUB_TYPE_PERAG:
436 estimate += ctx->mnt.fsgeom.agcount;
437 break;
438 case XFROG_SCRUB_TYPE_FS:
439 estimate++;
440 break;
441 default:
442 break;
443 }
444 }
445 return estimate;
446 }
447
448 /*
449 * Scrub file metadata of some sort. If errors occur, this function will log
450 * them and return nonzero.
451 */
452 int
453 scrub_file(
454 struct scrub_ctx *ctx,
455 int fd,
456 const struct xfs_bulkstat *bstat,
457 unsigned int type,
458 struct action_list *alist)
459 {
460 struct xfs_scrub_metadata meta = {0};
461 struct xfs_fd xfd;
462 struct xfs_fd *xfdp = &ctx->mnt;
463 enum check_outcome fix;
464
465 assert(type < XFS_SCRUB_TYPE_NR);
466 assert(xfrog_scrubbers[type].type == XFROG_SCRUB_TYPE_INODE);
467
468 meta.sm_type = type;
469 meta.sm_ino = bstat->bs_ino;
470 meta.sm_gen = bstat->bs_gen;
471
472 /*
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.
476 */
477 if (fd >= 0) {
478 memcpy(&xfd, xfdp, sizeof(xfd));
479 xfd.fd = fd;
480 xfdp = &xfd;
481 }
482
483 /* Scrub the piece of metadata. */
484 fix = xfs_check_metadata(ctx, xfdp, &meta, true);
485 if (fix == CHECK_ABORT)
486 return ECANCELED;
487 if (fix == CHECK_DONE)
488 return 0;
489
490 return scrub_save_repair(ctx, alist, &meta);
491 }
492
493 /*
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
496 * return false.
497 */
498 static bool
499 __scrub_test(
500 struct scrub_ctx *ctx,
501 unsigned int type,
502 unsigned int flags)
503 {
504 struct xfs_scrub_metadata meta = {0};
505 int error;
506
507 if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
508 return false;
509
510 meta.sm_type = type;
511 meta.sm_flags = flags;
512 error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
513 switch (error) {
514 case 0:
515 return true;
516 case EROFS:
517 str_info(ctx, ctx->mntpoint,
518 _("Filesystem is mounted read-only; cannot proceed."));
519 return false;
520 case ENOTRECOVERABLE:
521 str_info(ctx, ctx->mntpoint,
522 _("Filesystem is mounted norecovery; cannot proceed."));
523 return false;
524 case EINVAL:
525 case EOPNOTSUPP:
526 case ENOTTY:
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"));
533 return false;
534 case ENOENT:
535 /* Scrubber says not present on this fs; that's fine. */
536 return true;
537 default:
538 str_info(ctx, ctx->mntpoint, "%s", strerror(errno));
539 return true;
540 }
541 }
542
543 bool
544 can_scrub_fs_metadata(
545 struct scrub_ctx *ctx)
546 {
547 return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, 0);
548 }
549
550 bool
551 can_scrub_inode(
552 struct scrub_ctx *ctx)
553 {
554 return __scrub_test(ctx, XFS_SCRUB_TYPE_INODE, 0);
555 }
556
557 bool
558 can_scrub_bmap(
559 struct scrub_ctx *ctx)
560 {
561 return __scrub_test(ctx, XFS_SCRUB_TYPE_BMBTD, 0);
562 }
563
564 bool
565 can_scrub_dir(
566 struct scrub_ctx *ctx)
567 {
568 return __scrub_test(ctx, XFS_SCRUB_TYPE_DIR, 0);
569 }
570
571 bool
572 can_scrub_attr(
573 struct scrub_ctx *ctx)
574 {
575 return __scrub_test(ctx, XFS_SCRUB_TYPE_XATTR, 0);
576 }
577
578 bool
579 can_scrub_symlink(
580 struct scrub_ctx *ctx)
581 {
582 return __scrub_test(ctx, XFS_SCRUB_TYPE_SYMLINK, 0);
583 }
584
585 bool
586 can_scrub_parent(
587 struct scrub_ctx *ctx)
588 {
589 return __scrub_test(ctx, XFS_SCRUB_TYPE_PARENT, 0);
590 }
591
592 bool
593 xfs_can_repair(
594 struct scrub_ctx *ctx)
595 {
596 return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, XFS_SCRUB_IFLAG_REPAIR);
597 }
598
599 bool
600 can_force_rebuild(
601 struct scrub_ctx *ctx)
602 {
603 return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE,
604 XFS_SCRUB_IFLAG_REPAIR | XFS_SCRUB_IFLAG_FORCE_REBUILD);
605 }
606
607 /* General repair routines. */
608
609 /* Repair some metadata. */
610 enum check_outcome
611 xfs_repair_metadata(
612 struct scrub_ctx *ctx,
613 struct xfs_fd *xfdp,
614 struct action_item *aitem,
615 unsigned int repair_flags)
616 {
617 struct xfs_scrub_metadata meta = { 0 };
618 struct xfs_scrub_metadata oldm;
619 DEFINE_DESCR(dsc, ctx, format_scrub_descr);
620 int error;
621
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;
632 break;
633 case XFROG_SCRUB_TYPE_INODE:
634 meta.sm_ino = aitem->ino;
635 meta.sm_gen = aitem->gen;
636 break;
637 default:
638 break;
639 }
640
641 if (!is_corrupt(&meta) && (repair_flags & XRM_REPAIR_ONLY))
642 return CHECK_RETRY;
643
644 memcpy(&oldm, &meta, sizeof(oldm));
645 descr_set(&dsc, &oldm);
646
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."));
652
653 error = -xfrog_scrub_metadata(xfdp, &meta);
654 switch (error) {
655 case 0:
656 /* No operational errors encountered. */
657 break;
658 case EDEADLOCK:
659 case EBUSY:
660 /* Filesystem is busy, try again later. */
661 if (debug || verbose)
662 str_info(ctx, descr_render(&dsc),
663 _("Filesystem is busy, deferring repair."));
664 return CHECK_RETRY;
665 case ESHUTDOWN:
666 /* Filesystem is already shut down, abort. */
667 str_error(ctx, descr_render(&dsc),
668 _("Filesystem is shut down, aborting."));
669 return CHECK_ABORT;
670 case ENOTTY:
671 case EOPNOTSUPP:
672 /*
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.
677 */
678 if (is_unoptimized(&oldm) ||
679 debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
680 return CHECK_DONE;
681 /*
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.
688 */
689 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
690 return CHECK_RETRY;
691 fallthrough;
692 case EINVAL:
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."));
696 return CHECK_DONE;
697 case EROFS:
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."));
702 return CHECK_ABORT;
703 case ENOENT:
704 /* Metadata not present, just skip it. */
705 return CHECK_DONE;
706 case ENOMEM:
707 case ENOSPC:
708 /* Don't care if preen fails due to low resources. */
709 if (is_unoptimized(&oldm) && !needs_repair(&oldm))
710 return CHECK_DONE;
711 fallthrough;
712 default:
713 /*
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.
718 */
719 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
720 return CHECK_RETRY;
721 str_liberror(ctx, error, descr_render(&dsc));
722 return CHECK_DONE;
723 }
724
725 if (repair_flags & XRM_COMPLAIN_IF_UNFIXED)
726 scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
727 if (needs_repair(&meta)) {
728 /*
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.
732 */
733 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
734 return CHECK_RETRY;
735 str_corrupt(ctx, descr_render(&dsc),
736 _("Repair unsuccessful; offline repair required."));
737 } else if (xref_failed(&meta)) {
738 /*
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.
744 */
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."));
748 } else {
749 if (verbose)
750 str_info(ctx, descr_render(&dsc),
751 _("Seems correct but cross-referencing failed; will keep checking."));
752 return CHECK_RETRY;
753 }
754 } else {
755 /* Clean operation, no corruption detected. */
756 if (needs_repair(&oldm))
757 record_repair(ctx, descr_render(&dsc),
758 _("Repairs successful."));
759 else
760 record_preen(ctx, descr_render(&dsc),
761 _("Optimization successful."));
762 }
763 return CHECK_DONE;
764 }