]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/scrub.c
xfs: preserve rmapbt swapext block reservation from freed blocks
[thirdparty/xfsprogs-dev.git] / scrub / scrub.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
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 "xfs_errortag.h"
22 #include "repair.h"
23 #include "descr.h"
24
25 /* Online scrub and repair wrappers. */
26
27 /* Format a scrub description. */
28 static int
29 format_scrub_descr(
30 struct scrub_ctx *ctx,
31 char *buf,
32 size_t buflen,
33 void *where)
34 {
35 struct xfs_scrub_metadata *meta = where;
36 const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[meta->sm_type];
37
38 switch (sc->type) {
39 case XFROG_SCRUB_TYPE_AGHEADER:
40 case XFROG_SCRUB_TYPE_PERAG:
41 return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
42 _(sc->descr));
43 break;
44 case XFROG_SCRUB_TYPE_INODE:
45 return scrub_render_ino_descr(ctx, buf, buflen,
46 meta->sm_ino, meta->sm_gen, "%s",
47 _(sc->descr));
48 break;
49 case XFROG_SCRUB_TYPE_FS:
50 return snprintf(buf, buflen, _("%s"), _(sc->descr));
51 break;
52 case XFROG_SCRUB_TYPE_NONE:
53 assert(0);
54 break;
55 }
56 return -1;
57 }
58
59 /* Predicates for scrub flag state. */
60
61 static inline bool is_corrupt(struct xfs_scrub_metadata *sm)
62 {
63 return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT;
64 }
65
66 static inline bool is_unoptimized(struct xfs_scrub_metadata *sm)
67 {
68 return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN;
69 }
70
71 static inline bool xref_failed(struct xfs_scrub_metadata *sm)
72 {
73 return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL;
74 }
75
76 static inline bool xref_disagrees(struct xfs_scrub_metadata *sm)
77 {
78 return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT;
79 }
80
81 static inline bool is_incomplete(struct xfs_scrub_metadata *sm)
82 {
83 return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
84 }
85
86 static inline bool is_suspicious(struct xfs_scrub_metadata *sm)
87 {
88 return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING;
89 }
90
91 /* Should we fix it? */
92 static inline bool needs_repair(struct xfs_scrub_metadata *sm)
93 {
94 return is_corrupt(sm) || xref_disagrees(sm);
95 }
96
97 /* Warn about strange circumstances after scrub. */
98 static inline void
99 scrub_warn_incomplete_scrub(
100 struct scrub_ctx *ctx,
101 struct descr *dsc,
102 struct xfs_scrub_metadata *meta)
103 {
104 if (is_incomplete(meta))
105 str_info(ctx, descr_render(dsc), _("Check incomplete."));
106
107 if (is_suspicious(meta)) {
108 if (debug)
109 str_info(ctx, descr_render(dsc),
110 _("Possibly suspect metadata."));
111 else
112 str_warn(ctx, descr_render(dsc),
113 _("Possibly suspect metadata."));
114 }
115
116 if (xref_failed(meta))
117 str_info(ctx, descr_render(dsc),
118 _("Cross-referencing failed."));
119 }
120
121 /* Do a read-only check of some metadata. */
122 static enum check_outcome
123 xfs_check_metadata(
124 struct scrub_ctx *ctx,
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(&ctx->mnt, 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_errno(ctx, 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 /* fall through */
168 default:
169 /* Operational error. */
170 str_errno(ctx, 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 /* Everything is ok. */
227 return CHECK_DONE;
228 }
229
230 /* Bulk-notify user about things that could be optimized. */
231 void
232 scrub_report_preen_triggers(
233 struct scrub_ctx *ctx)
234 {
235 int i;
236
237 for (i = 0; i < XFS_SCRUB_TYPE_NR; i++) {
238 pthread_mutex_lock(&ctx->lock);
239 if (ctx->preen_triggers[i]) {
240 ctx->preen_triggers[i] = false;
241 pthread_mutex_unlock(&ctx->lock);
242 str_info(ctx, ctx->mntpoint,
243 _("Optimizations of %s are possible."), _(xfrog_scrubbers[i].descr));
244 } else {
245 pthread_mutex_unlock(&ctx->lock);
246 }
247 }
248 }
249
250 /* Save a scrub context for later repairs. */
251 static int
252 scrub_save_repair(
253 struct scrub_ctx *ctx,
254 struct action_list *alist,
255 struct xfs_scrub_metadata *meta)
256 {
257 struct action_item *aitem;
258
259 /* Schedule this item for later repairs. */
260 aitem = malloc(sizeof(struct action_item));
261 if (!aitem) {
262 str_errno(ctx, _("adding item to repair list"));
263 return errno;
264 }
265
266 memset(aitem, 0, sizeof(*aitem));
267 aitem->type = meta->sm_type;
268 aitem->flags = meta->sm_flags;
269 switch (xfrog_scrubbers[meta->sm_type].type) {
270 case XFROG_SCRUB_TYPE_AGHEADER:
271 case XFROG_SCRUB_TYPE_PERAG:
272 aitem->agno = meta->sm_agno;
273 break;
274 case XFROG_SCRUB_TYPE_INODE:
275 aitem->ino = meta->sm_ino;
276 aitem->gen = meta->sm_gen;
277 break;
278 default:
279 break;
280 }
281
282 action_list_add(alist, aitem);
283 return 0;
284 }
285
286 /*
287 * Scrub a single XFS_SCRUB_TYPE_*, saving corruption reports for later.
288 *
289 * Returns 0 for success. If errors occur, this function will log them and
290 * return a positive error code.
291 */
292 static int
293 scrub_meta_type(
294 struct scrub_ctx *ctx,
295 unsigned int type,
296 xfs_agnumber_t agno,
297 struct action_list *alist)
298 {
299 struct xfs_scrub_metadata meta = {
300 .sm_type = type,
301 .sm_agno = agno,
302 };
303 enum check_outcome fix;
304 int ret;
305
306 background_sleep();
307
308 /* Check the item. */
309 fix = xfs_check_metadata(ctx, &meta, false);
310 progress_add(1);
311
312 switch (fix) {
313 case CHECK_ABORT:
314 return ECANCELED;
315 case CHECK_REPAIR:
316 ret = scrub_save_repair(ctx, alist, &meta);
317 if (ret)
318 return ret;
319 /* fall through */
320 case CHECK_DONE:
321 return 0;
322 default:
323 /* CHECK_RETRY should never happen. */
324 abort();
325 }
326 }
327
328 /*
329 * Scrub all metadata types that are assigned to the given XFROG_SCRUB_TYPE_*,
330 * saving corruption reports for later. This should not be used for
331 * XFROG_SCRUB_TYPE_INODE or for checking summary metadata.
332 */
333 static bool
334 scrub_all_types(
335 struct scrub_ctx *ctx,
336 enum xfrog_scrub_type scrub_type,
337 xfs_agnumber_t agno,
338 struct action_list *alist)
339 {
340 const struct xfrog_scrub_descr *sc;
341 unsigned int type;
342
343 sc = xfrog_scrubbers;
344 for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
345 int ret;
346
347 if (sc->type != scrub_type)
348 continue;
349 if (sc->flags & XFROG_SCRUB_DESCR_SUMMARY)
350 continue;
351
352 ret = scrub_meta_type(ctx, type, agno, alist);
353 if (ret)
354 return ret;
355 }
356
357 return 0;
358 }
359
360 /*
361 * Scrub primary superblock. This will be useful if we ever need to hook
362 * a filesystem-wide pre-scrub activity off of the sb 0 scrubber (which
363 * currently does nothing). If errors occur, this function will log them and
364 * return nonzero.
365 */
366 int
367 scrub_primary_super(
368 struct scrub_ctx *ctx,
369 struct action_list *alist)
370 {
371 return scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, alist);
372 }
373
374 /* Scrub each AG's header blocks. */
375 int
376 scrub_ag_headers(
377 struct scrub_ctx *ctx,
378 xfs_agnumber_t agno,
379 struct action_list *alist)
380 {
381 return scrub_all_types(ctx, XFROG_SCRUB_TYPE_AGHEADER, agno, alist);
382 }
383
384 /* Scrub each AG's metadata btrees. */
385 int
386 scrub_ag_metadata(
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_PERAG, agno, alist);
392 }
393
394 /* Scrub whole-FS metadata btrees. */
395 int
396 scrub_fs_metadata(
397 struct scrub_ctx *ctx,
398 struct action_list *alist)
399 {
400 return scrub_all_types(ctx, XFROG_SCRUB_TYPE_FS, 0, alist);
401 }
402
403 /* Scrub FS summary metadata. */
404 int
405 scrub_fs_summary(
406 struct scrub_ctx *ctx,
407 struct action_list *alist)
408 {
409 return scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, alist);
410 }
411
412 /* How many items do we have to check? */
413 unsigned int
414 scrub_estimate_ag_work(
415 struct scrub_ctx *ctx)
416 {
417 const struct xfrog_scrub_descr *sc;
418 int type;
419 unsigned int estimate = 0;
420
421 sc = xfrog_scrubbers;
422 for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
423 switch (sc->type) {
424 case XFROG_SCRUB_TYPE_AGHEADER:
425 case XFROG_SCRUB_TYPE_PERAG:
426 estimate += ctx->mnt.fsgeom.agcount;
427 break;
428 case XFROG_SCRUB_TYPE_FS:
429 estimate++;
430 break;
431 default:
432 break;
433 }
434 }
435 return estimate;
436 }
437
438 /*
439 * Scrub inode metadata. If errors occur, this function will log them and
440 * return nonzero.
441 */
442 static int
443 __scrub_file(
444 struct scrub_ctx *ctx,
445 uint64_t ino,
446 uint32_t gen,
447 unsigned int type,
448 struct action_list *alist)
449 {
450 struct xfs_scrub_metadata meta = {0};
451 enum check_outcome fix;
452
453 assert(type < XFS_SCRUB_TYPE_NR);
454 assert(xfrog_scrubbers[type].type == XFROG_SCRUB_TYPE_INODE);
455
456 meta.sm_type = type;
457 meta.sm_ino = ino;
458 meta.sm_gen = gen;
459
460 /* Scrub the piece of metadata. */
461 fix = xfs_check_metadata(ctx, &meta, true);
462 if (fix == CHECK_ABORT)
463 return ECANCELED;
464 if (fix == CHECK_DONE)
465 return 0;
466
467 return scrub_save_repair(ctx, alist, &meta);
468 }
469
470 int
471 scrub_inode_fields(
472 struct scrub_ctx *ctx,
473 uint64_t ino,
474 uint32_t gen,
475 struct action_list *alist)
476 {
477 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_INODE, alist);
478 }
479
480 int
481 scrub_data_fork(
482 struct scrub_ctx *ctx,
483 uint64_t ino,
484 uint32_t gen,
485 struct action_list *alist)
486 {
487 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTD, alist);
488 }
489
490 int
491 scrub_attr_fork(
492 struct scrub_ctx *ctx,
493 uint64_t ino,
494 uint32_t gen,
495 struct action_list *alist)
496 {
497 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTA, alist);
498 }
499
500 int
501 scrub_cow_fork(
502 struct scrub_ctx *ctx,
503 uint64_t ino,
504 uint32_t gen,
505 struct action_list *alist)
506 {
507 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTC, alist);
508 }
509
510 int
511 scrub_dir(
512 struct scrub_ctx *ctx,
513 uint64_t ino,
514 uint32_t gen,
515 struct action_list *alist)
516 {
517 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_DIR, alist);
518 }
519
520 int
521 scrub_attr(
522 struct scrub_ctx *ctx,
523 uint64_t ino,
524 uint32_t gen,
525 struct action_list *alist)
526 {
527 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_XATTR, alist);
528 }
529
530 int
531 scrub_symlink(
532 struct scrub_ctx *ctx,
533 uint64_t ino,
534 uint32_t gen,
535 struct action_list *alist)
536 {
537 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_SYMLINK, alist);
538 }
539
540 int
541 scrub_parent(
542 struct scrub_ctx *ctx,
543 uint64_t ino,
544 uint32_t gen,
545 struct action_list *alist)
546 {
547 return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_PARENT, alist);
548 }
549
550 /*
551 * Test the availability of a kernel scrub command. If errors occur (or the
552 * scrub ioctl is rejected) the errors will be logged and this function will
553 * return false.
554 */
555 static bool
556 __scrub_test(
557 struct scrub_ctx *ctx,
558 unsigned int type,
559 bool repair)
560 {
561 struct xfs_scrub_metadata meta = {0};
562 struct xfs_error_injection inject;
563 static bool injected;
564 int error;
565
566 if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
567 return false;
568 if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !injected) {
569 inject.fd = ctx->mnt.fd;
570 inject.errtag = XFS_ERRTAG_FORCE_SCRUB_REPAIR;
571 error = ioctl(ctx->mnt.fd, XFS_IOC_ERROR_INJECTION, &inject);
572 if (error == 0)
573 injected = true;
574 }
575
576 meta.sm_type = type;
577 if (repair)
578 meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
579 error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
580 switch (error) {
581 case 0:
582 return true;
583 case EROFS:
584 str_info(ctx, ctx->mntpoint,
585 _("Filesystem is mounted read-only; cannot proceed."));
586 return false;
587 case ENOTRECOVERABLE:
588 str_info(ctx, ctx->mntpoint,
589 _("Filesystem is mounted norecovery; cannot proceed."));
590 return false;
591 case EOPNOTSUPP:
592 case ENOTTY:
593 if (debug || verbose)
594 str_info(ctx, ctx->mntpoint,
595 _("Kernel %s %s facility not detected."),
596 _(xfrog_scrubbers[type].descr),
597 repair ? _("repair") : _("scrub"));
598 return false;
599 case ENOENT:
600 /* Scrubber says not present on this fs; that's fine. */
601 return true;
602 default:
603 str_info(ctx, ctx->mntpoint, "%s", strerror(errno));
604 return true;
605 }
606 }
607
608 bool
609 can_scrub_fs_metadata(
610 struct scrub_ctx *ctx)
611 {
612 return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, false);
613 }
614
615 bool
616 can_scrub_inode(
617 struct scrub_ctx *ctx)
618 {
619 return __scrub_test(ctx, XFS_SCRUB_TYPE_INODE, false);
620 }
621
622 bool
623 can_scrub_bmap(
624 struct scrub_ctx *ctx)
625 {
626 return __scrub_test(ctx, XFS_SCRUB_TYPE_BMBTD, false);
627 }
628
629 bool
630 can_scrub_dir(
631 struct scrub_ctx *ctx)
632 {
633 return __scrub_test(ctx, XFS_SCRUB_TYPE_DIR, false);
634 }
635
636 bool
637 can_scrub_attr(
638 struct scrub_ctx *ctx)
639 {
640 return __scrub_test(ctx, XFS_SCRUB_TYPE_XATTR, false);
641 }
642
643 bool
644 can_scrub_symlink(
645 struct scrub_ctx *ctx)
646 {
647 return __scrub_test(ctx, XFS_SCRUB_TYPE_SYMLINK, false);
648 }
649
650 bool
651 can_scrub_parent(
652 struct scrub_ctx *ctx)
653 {
654 return __scrub_test(ctx, XFS_SCRUB_TYPE_PARENT, false);
655 }
656
657 bool
658 xfs_can_repair(
659 struct scrub_ctx *ctx)
660 {
661 return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, true);
662 }
663
664 /* General repair routines. */
665
666 /* Repair some metadata. */
667 enum check_outcome
668 xfs_repair_metadata(
669 struct scrub_ctx *ctx,
670 int fd,
671 struct action_item *aitem,
672 unsigned int repair_flags)
673 {
674 struct xfs_scrub_metadata meta = { 0 };
675 struct xfs_scrub_metadata oldm;
676 DEFINE_DESCR(dsc, ctx, format_scrub_descr);
677 int error;
678
679 assert(aitem->type < XFS_SCRUB_TYPE_NR);
680 assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
681 meta.sm_type = aitem->type;
682 meta.sm_flags = aitem->flags | XFS_SCRUB_IFLAG_REPAIR;
683 switch (xfrog_scrubbers[aitem->type].type) {
684 case XFROG_SCRUB_TYPE_AGHEADER:
685 case XFROG_SCRUB_TYPE_PERAG:
686 meta.sm_agno = aitem->agno;
687 break;
688 case XFROG_SCRUB_TYPE_INODE:
689 meta.sm_ino = aitem->ino;
690 meta.sm_gen = aitem->gen;
691 break;
692 default:
693 break;
694 }
695
696 if (!is_corrupt(&meta) && (repair_flags & XRM_REPAIR_ONLY))
697 return CHECK_RETRY;
698
699 memcpy(&oldm, &meta, sizeof(oldm));
700 descr_set(&dsc, &oldm);
701
702 if (needs_repair(&meta))
703 str_info(ctx, descr_render(&dsc), _("Attempting repair."));
704 else if (debug || verbose)
705 str_info(ctx, descr_render(&dsc),
706 _("Attempting optimization."));
707
708 error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
709 switch (error) {
710 case 0:
711 /* No operational errors encountered. */
712 break;
713 case EDEADLOCK:
714 case EBUSY:
715 /* Filesystem is busy, try again later. */
716 if (debug || verbose)
717 str_info(ctx, descr_render(&dsc),
718 _("Filesystem is busy, deferring repair."));
719 return CHECK_RETRY;
720 case ESHUTDOWN:
721 /* Filesystem is already shut down, abort. */
722 str_error(ctx, descr_render(&dsc),
723 _("Filesystem is shut down, aborting."));
724 return CHECK_ABORT;
725 case ENOTTY:
726 case EOPNOTSUPP:
727 /*
728 * If we're in no-complain mode, requeue the check for
729 * later. It's possible that an error in another
730 * component caused us to flag an error in this
731 * component. Even if the kernel didn't think it
732 * could fix this, it's at least worth trying the scan
733 * again to see if another repair fixed it.
734 */
735 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
736 return CHECK_RETRY;
737 /*
738 * If we forced repairs or this is a preen, don't
739 * error out if the kernel doesn't know how to fix.
740 */
741 if (is_unoptimized(&oldm) ||
742 debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
743 return CHECK_DONE;
744 /* fall through */
745 case EINVAL:
746 /* Kernel doesn't know how to repair this? */
747 str_corrupt(ctx, descr_render(&dsc),
748 _("Don't know how to fix; offline repair required."));
749 return CHECK_DONE;
750 case EROFS:
751 /* Read-only filesystem, can't fix. */
752 if (verbose || debug || needs_repair(&oldm))
753 str_error(ctx, descr_render(&dsc),
754 _("Read-only filesystem; cannot make changes."));
755 return CHECK_ABORT;
756 case ENOENT:
757 /* Metadata not present, just skip it. */
758 return CHECK_DONE;
759 case ENOMEM:
760 case ENOSPC:
761 /* Don't care if preen fails due to low resources. */
762 if (is_unoptimized(&oldm) && !needs_repair(&oldm))
763 return CHECK_DONE;
764 /* fall through */
765 default:
766 /*
767 * Operational error. If the caller doesn't want us
768 * to complain about repair failures, tell the caller
769 * to requeue the repair for later and don't say a
770 * thing. Otherwise, print error and bail out.
771 */
772 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
773 return CHECK_RETRY;
774 str_liberror(ctx, error, descr_render(&dsc));
775 return CHECK_DONE;
776 }
777
778 if (repair_flags & XRM_COMPLAIN_IF_UNFIXED)
779 scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
780 if (needs_repair(&meta)) {
781 /*
782 * Still broken; if we've been told not to complain then we
783 * just requeue this and try again later. Otherwise we
784 * log the error loudly and don't try again.
785 */
786 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
787 return CHECK_RETRY;
788 str_corrupt(ctx, descr_render(&dsc),
789 _("Repair unsuccessful; offline repair required."));
790 } else {
791 /* Clean operation, no corruption detected. */
792 if (needs_repair(&oldm))
793 record_repair(ctx, descr_render(&dsc),
794 _("Repairs successful."));
795 else
796 record_preen(ctx, descr_render(&dsc),
797 _("Optimization successful."));
798 }
799 return CHECK_DONE;
800 }