]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/scrub.c
libfrog: add online scrub/repair for superblock counters
[thirdparty/xfsprogs-dev.git] / scrub / scrub.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
fd7d73c0
DW
2/*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
fd7d73c0 4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
fd7d73c0 5 */
a440f877 6#include "xfs.h"
fd7d73c0
DW
7#include <stdint.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <string.h>
11#include <sys/types.h>
fd7d73c0 12#include <sys/statvfs.h>
19852474 13#include "list.h"
42b4c8e8 14#include "libfrog/paths.h"
5ef3b66a
DW
15#include "libfrog/fsgeom.h"
16#include "libfrog/scrub.h"
fd7d73c0
DW
17#include "xfs_scrub.h"
18#include "common.h"
ed60d210 19#include "progress.h"
fd7d73c0
DW
20#include "scrub.h"
21#include "xfs_errortag.h"
ee310b0c 22#include "repair.h"
fd7d73c0
DW
23
24/* Online scrub and repair wrappers. */
25
fd7d73c0
DW
26/* Format a scrub description. */
27static void
28format_scrub_descr(
29 char *buf,
30 size_t buflen,
31 struct xfs_scrub_metadata *meta,
5ef3b66a 32 const struct xfrog_scrub_descr *sc)
fd7d73c0
DW
33{
34 switch (sc->type) {
5ef3b66a
DW
35 case XFROG_SCRUB_TYPE_AGHEADER:
36 case XFROG_SCRUB_TYPE_PERAG:
fd7d73c0 37 snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
5ef3b66a 38 _(sc->descr));
fd7d73c0 39 break;
5ef3b66a 40 case XFROG_SCRUB_TYPE_INODE:
fd7d73c0 41 snprintf(buf, buflen, _("Inode %"PRIu64" %s"),
5ef3b66a 42 (uint64_t)meta->sm_ino, _(sc->descr));
fd7d73c0 43 break;
5ef3b66a
DW
44 case XFROG_SCRUB_TYPE_FS:
45 snprintf(buf, buflen, _("%s"), _(sc->descr));
fd7d73c0 46 break;
5ef3b66a 47 case XFROG_SCRUB_TYPE_NONE:
fd7d73c0
DW
48 assert(0);
49 break;
50 }
51}
52
53/* Predicates for scrub flag state. */
54
55static inline bool is_corrupt(struct xfs_scrub_metadata *sm)
56{
57 return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT;
58}
59
60static inline bool is_unoptimized(struct xfs_scrub_metadata *sm)
61{
62 return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN;
63}
64
65static inline bool xref_failed(struct xfs_scrub_metadata *sm)
66{
67 return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL;
68}
69
70static inline bool xref_disagrees(struct xfs_scrub_metadata *sm)
71{
72 return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT;
73}
74
75static inline bool is_incomplete(struct xfs_scrub_metadata *sm)
76{
77 return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
78}
79
80static inline bool is_suspicious(struct xfs_scrub_metadata *sm)
81{
82 return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING;
83}
84
85/* Should we fix it? */
86static inline bool needs_repair(struct xfs_scrub_metadata *sm)
87{
88 return is_corrupt(sm) || xref_disagrees(sm);
89}
90
91/* Warn about strange circumstances after scrub. */
92static inline void
93xfs_scrub_warn_incomplete_scrub(
94 struct scrub_ctx *ctx,
95 const char *descr,
96 struct xfs_scrub_metadata *meta)
97{
98 if (is_incomplete(meta))
99 str_info(ctx, descr, _("Check incomplete."));
100
101 if (is_suspicious(meta)) {
102 if (debug)
103 str_info(ctx, descr, _("Possibly suspect metadata."));
104 else
105 str_warn(ctx, descr, _("Possibly suspect metadata."));
106 }
107
108 if (xref_failed(meta))
109 str_info(ctx, descr, _("Cross-referencing failed."));
110}
111
112/* Do a read-only check of some metadata. */
113static enum check_outcome
114xfs_check_metadata(
115 struct scrub_ctx *ctx,
fd7d73c0
DW
116 struct xfs_scrub_metadata *meta,
117 bool is_inode)
118{
119 char buf[DESCR_BUFSZ];
120 unsigned int tries = 0;
121 int code;
122 int error;
123
124 assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
125 assert(meta->sm_type < XFS_SCRUB_TYPE_NR);
5ef3b66a
DW
126 format_scrub_descr(buf, DESCR_BUFSZ, meta,
127 &xfrog_scrubbers[meta->sm_type]);
fd7d73c0
DW
128
129 dbg_printf("check %s flags %xh\n", buf, meta->sm_flags);
130retry:
5ef3b66a 131 error = xfrog_scrub_metadata(&ctx->mnt, meta);
fd7d73c0
DW
132 if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
133 meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
134 if (error) {
135 code = errno;
136 switch (code) {
137 case ENOENT:
138 /* Metadata not present, just skip it. */
139 return CHECK_DONE;
140 case ESHUTDOWN:
141 /* FS already crashed, give up. */
82377bde 142 str_info(ctx, buf,
fd7d73c0
DW
143_("Filesystem is shut down, aborting."));
144 return CHECK_ABORT;
145 case EIO:
146 case ENOMEM:
147 /* Abort on I/O errors or insufficient memory. */
148 str_errno(ctx, buf);
149 return CHECK_ABORT;
150 case EDEADLOCK:
151 case EBUSY:
152 case EFSBADCRC:
153 case EFSCORRUPTED:
154 /*
155 * The first two should never escape the kernel,
156 * and the other two should be reported via sm_flags.
157 */
82377bde 158 str_info(ctx, buf,
fd7d73c0
DW
159_("Kernel bug! errno=%d"), code);
160 /* fall through */
161 default:
162 /* Operational error. */
163 str_errno(ctx, buf);
164 return CHECK_DONE;
165 }
166 }
167
168 /*
169 * If the kernel says the test was incomplete or that there was
170 * a cross-referencing discrepancy but no obvious corruption,
171 * we'll try the scan again, just in case the fs was busy.
172 * Only retry so many times.
173 */
174 if (tries < 10 && (is_incomplete(meta) ||
175 (xref_disagrees(meta) && !is_corrupt(meta)))) {
176 tries++;
177 goto retry;
178 }
179
180 /* Complain about incomplete or suspicious metadata. */
181 xfs_scrub_warn_incomplete_scrub(ctx, buf, meta);
182
183 /*
184 * If we need repairs or there were discrepancies, schedule a
185 * repair if desired, otherwise complain.
186 */
187 if (is_corrupt(meta) || xref_disagrees(meta)) {
188 if (ctx->mode < SCRUB_MODE_REPAIR) {
189 str_error(ctx, buf,
190_("Repairs are required."));
191 return CHECK_DONE;
192 }
193
194 return CHECK_REPAIR;
195 }
196
197 /*
198 * If we could optimize, schedule a repair if desired,
199 * otherwise complain.
200 */
201 if (is_unoptimized(meta)) {
1658224d 202 if (ctx->mode != SCRUB_MODE_REPAIR) {
fd7d73c0
DW
203 if (!is_inode) {
204 /* AG or FS metadata, always warn. */
205 str_info(ctx, buf,
206_("Optimization is possible."));
207 } else if (!ctx->preen_triggers[meta->sm_type]) {
208 /* File metadata, only warn once per type. */
209 pthread_mutex_lock(&ctx->lock);
210 if (!ctx->preen_triggers[meta->sm_type])
211 ctx->preen_triggers[meta->sm_type] = true;
212 pthread_mutex_unlock(&ctx->lock);
213 }
214 return CHECK_DONE;
215 }
216
217 return CHECK_REPAIR;
218 }
219
220 /* Everything is ok. */
221 return CHECK_DONE;
222}
223
224/* Bulk-notify user about things that could be optimized. */
225void
226xfs_scrub_report_preen_triggers(
227 struct scrub_ctx *ctx)
228{
229 int i;
230
231 for (i = 0; i < XFS_SCRUB_TYPE_NR; i++) {
232 pthread_mutex_lock(&ctx->lock);
233 if (ctx->preen_triggers[i]) {
234 ctx->preen_triggers[i] = false;
235 pthread_mutex_unlock(&ctx->lock);
236 str_info(ctx, ctx->mntpoint,
5ef3b66a 237_("Optimizations of %s are possible."), _(xfrog_scrubbers[i].descr));
fd7d73c0
DW
238 } else {
239 pthread_mutex_unlock(&ctx->lock);
240 }
241 }
242}
243
ee310b0c 244/* Save a scrub context for later repairs. */
00ff2b10 245static bool
ee310b0c
DW
246xfs_scrub_save_repair(
247 struct scrub_ctx *ctx,
248 struct xfs_action_list *alist,
249 struct xfs_scrub_metadata *meta)
250{
251 struct action_item *aitem;
252
253 /* Schedule this item for later repairs. */
254 aitem = malloc(sizeof(struct action_item));
255 if (!aitem) {
256 str_errno(ctx, _("repair list"));
257 return false;
258 }
259 memset(aitem, 0, sizeof(*aitem));
260 aitem->type = meta->sm_type;
261 aitem->flags = meta->sm_flags;
5ef3b66a
DW
262 switch (xfrog_scrubbers[meta->sm_type].type) {
263 case XFROG_SCRUB_TYPE_AGHEADER:
264 case XFROG_SCRUB_TYPE_PERAG:
ee310b0c
DW
265 aitem->agno = meta->sm_agno;
266 break;
5ef3b66a 267 case XFROG_SCRUB_TYPE_INODE:
ee310b0c
DW
268 aitem->ino = meta->sm_ino;
269 aitem->gen = meta->sm_gen;
270 break;
271 default:
272 break;
273 }
274
275 xfs_action_list_add(alist, aitem);
276 return true;
277}
278
fd7d73c0
DW
279/* Scrub metadata, saving corruption reports for later. */
280static bool
281xfs_scrub_metadata(
282 struct scrub_ctx *ctx,
5ef3b66a 283 enum xfrog_scrub_type scrub_type,
ee310b0c
DW
284 xfs_agnumber_t agno,
285 struct xfs_action_list *alist)
fd7d73c0
DW
286{
287 struct xfs_scrub_metadata meta = {0};
5ef3b66a 288 const struct xfrog_scrub_descr *sc;
fd7d73c0
DW
289 enum check_outcome fix;
290 int type;
291
5ef3b66a 292 sc = xfrog_scrubbers;
fd7d73c0
DW
293 for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
294 if (sc->type != scrub_type)
295 continue;
8dd3922c
DW
296 if (sc->flags & XFROG_SCRUB_DESCR_SUMMARY)
297 continue;
fd7d73c0
DW
298
299 meta.sm_type = type;
300 meta.sm_flags = 0;
301 meta.sm_agno = agno;
302 background_sleep();
303
304 /* Check the item. */
991e5a84 305 fix = xfs_check_metadata(ctx, &meta, false);
ed60d210 306 progress_add(1);
fd7d73c0
DW
307 switch (fix) {
308 case CHECK_ABORT:
309 return false;
310 case CHECK_REPAIR:
ee310b0c
DW
311 if (!xfs_scrub_save_repair(ctx, alist, &meta))
312 return false;
fd7d73c0
DW
313 /* fall through */
314 case CHECK_DONE:
315 continue;
316 case CHECK_RETRY:
317 abort();
318 break;
319 }
320 }
321
322 return true;
323}
324
325/*
326 * Scrub primary superblock. This will be useful if we ever need to hook
327 * a filesystem-wide pre-scrub activity off of the sb 0 scrubber (which
328 * currently does nothing).
329 */
330bool
331xfs_scrub_primary_super(
ee310b0c
DW
332 struct scrub_ctx *ctx,
333 struct xfs_action_list *alist)
fd7d73c0
DW
334{
335 struct xfs_scrub_metadata meta = {
336 .sm_type = XFS_SCRUB_TYPE_SB,
337 };
338 enum check_outcome fix;
339
340 /* Check the item. */
991e5a84 341 fix = xfs_check_metadata(ctx, &meta, false);
fd7d73c0
DW
342 switch (fix) {
343 case CHECK_ABORT:
344 return false;
345 case CHECK_REPAIR:
ee310b0c
DW
346 if (!xfs_scrub_save_repair(ctx, alist, &meta))
347 return false;
fd7d73c0
DW
348 /* fall through */
349 case CHECK_DONE:
350 return true;
351 case CHECK_RETRY:
352 abort();
353 break;
354 }
355
356 return true;
357}
358
359/* Scrub each AG's header blocks. */
360bool
361xfs_scrub_ag_headers(
362 struct scrub_ctx *ctx,
ee310b0c
DW
363 xfs_agnumber_t agno,
364 struct xfs_action_list *alist)
fd7d73c0 365{
5ef3b66a 366 return xfs_scrub_metadata(ctx, XFROG_SCRUB_TYPE_AGHEADER, agno, alist);
fd7d73c0
DW
367}
368
369/* Scrub each AG's metadata btrees. */
370bool
371xfs_scrub_ag_metadata(
372 struct scrub_ctx *ctx,
ee310b0c
DW
373 xfs_agnumber_t agno,
374 struct xfs_action_list *alist)
fd7d73c0 375{
5ef3b66a 376 return xfs_scrub_metadata(ctx, XFROG_SCRUB_TYPE_PERAG, agno, alist);
fd7d73c0
DW
377}
378
379/* Scrub whole-FS metadata btrees. */
380bool
381xfs_scrub_fs_metadata(
ee310b0c
DW
382 struct scrub_ctx *ctx,
383 struct xfs_action_list *alist)
fd7d73c0 384{
5ef3b66a 385 return xfs_scrub_metadata(ctx, XFROG_SCRUB_TYPE_FS, 0, alist);
fd7d73c0
DW
386}
387
ed60d210
DW
388/* How many items do we have to check? */
389unsigned int
390xfs_scrub_estimate_ag_work(
391 struct scrub_ctx *ctx)
392{
5ef3b66a 393 const struct xfrog_scrub_descr *sc;
ed60d210
DW
394 int type;
395 unsigned int estimate = 0;
396
5ef3b66a 397 sc = xfrog_scrubbers;
ed60d210
DW
398 for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
399 switch (sc->type) {
5ef3b66a
DW
400 case XFROG_SCRUB_TYPE_AGHEADER:
401 case XFROG_SCRUB_TYPE_PERAG:
3f9efb2e 402 estimate += ctx->mnt.fsgeom.agcount;
ed60d210 403 break;
5ef3b66a 404 case XFROG_SCRUB_TYPE_FS:
ed60d210
DW
405 estimate++;
406 break;
407 default:
408 break;
409 }
410 }
411 return estimate;
412}
413
fd7d73c0
DW
414/* Scrub inode metadata. */
415static bool
416__xfs_scrub_file(
417 struct scrub_ctx *ctx,
418 uint64_t ino,
419 uint32_t gen,
ee310b0c
DW
420 unsigned int type,
421 struct xfs_action_list *alist)
fd7d73c0
DW
422{
423 struct xfs_scrub_metadata meta = {0};
424 enum check_outcome fix;
425
426 assert(type < XFS_SCRUB_TYPE_NR);
5ef3b66a 427 assert(xfrog_scrubbers[type].type == XFROG_SCRUB_TYPE_INODE);
fd7d73c0
DW
428
429 meta.sm_type = type;
430 meta.sm_ino = ino;
431 meta.sm_gen = gen;
432
433 /* Scrub the piece of metadata. */
991e5a84 434 fix = xfs_check_metadata(ctx, &meta, true);
fd7d73c0
DW
435 if (fix == CHECK_ABORT)
436 return false;
437 if (fix == CHECK_DONE)
438 return true;
439
ee310b0c 440 return xfs_scrub_save_repair(ctx, alist, &meta);
fd7d73c0
DW
441}
442
443bool
444xfs_scrub_inode_fields(
445 struct scrub_ctx *ctx,
446 uint64_t ino,
447 uint32_t gen,
ee310b0c 448 struct xfs_action_list *alist)
fd7d73c0 449{
991e5a84 450 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_INODE, alist);
fd7d73c0
DW
451}
452
453bool
454xfs_scrub_data_fork(
455 struct scrub_ctx *ctx,
456 uint64_t ino,
457 uint32_t gen,
ee310b0c 458 struct xfs_action_list *alist)
fd7d73c0 459{
991e5a84 460 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTD, alist);
fd7d73c0
DW
461}
462
463bool
464xfs_scrub_attr_fork(
465 struct scrub_ctx *ctx,
466 uint64_t ino,
467 uint32_t gen,
ee310b0c 468 struct xfs_action_list *alist)
fd7d73c0 469{
991e5a84 470 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTA, alist);
fd7d73c0
DW
471}
472
473bool
474xfs_scrub_cow_fork(
475 struct scrub_ctx *ctx,
476 uint64_t ino,
477 uint32_t gen,
ee310b0c 478 struct xfs_action_list *alist)
fd7d73c0 479{
991e5a84 480 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTC, alist);
fd7d73c0
DW
481}
482
483bool
484xfs_scrub_dir(
485 struct scrub_ctx *ctx,
486 uint64_t ino,
487 uint32_t gen,
ee310b0c 488 struct xfs_action_list *alist)
fd7d73c0 489{
991e5a84 490 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_DIR, alist);
fd7d73c0
DW
491}
492
493bool
494xfs_scrub_attr(
495 struct scrub_ctx *ctx,
496 uint64_t ino,
497 uint32_t gen,
ee310b0c 498 struct xfs_action_list *alist)
fd7d73c0 499{
991e5a84 500 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_XATTR, alist);
fd7d73c0
DW
501}
502
503bool
504xfs_scrub_symlink(
505 struct scrub_ctx *ctx,
506 uint64_t ino,
507 uint32_t gen,
ee310b0c 508 struct xfs_action_list *alist)
fd7d73c0 509{
991e5a84 510 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_SYMLINK, alist);
fd7d73c0
DW
511}
512
513bool
514xfs_scrub_parent(
515 struct scrub_ctx *ctx,
516 uint64_t ino,
517 uint32_t gen,
ee310b0c 518 struct xfs_action_list *alist)
fd7d73c0 519{
991e5a84 520 return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_PARENT, alist);
fd7d73c0
DW
521}
522
523/* Test the availability of a kernel scrub command. */
524static bool
525__xfs_scrub_test(
526 struct scrub_ctx *ctx,
527 unsigned int type,
528 bool repair)
529{
530 struct xfs_scrub_metadata meta = {0};
7a9d1ac4 531 struct xfs_error_injection inject;
19852474 532 static bool injected;
fd7d73c0
DW
533 int error;
534
535 if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
536 return false;
19852474 537 if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !injected) {
3f9efb2e 538 inject.fd = ctx->mnt.fd;
7a9d1ac4 539 inject.errtag = XFS_ERRTAG_FORCE_SCRUB_REPAIR;
3f9efb2e 540 error = ioctl(ctx->mnt.fd, XFS_IOC_ERROR_INJECTION, &inject);
7a9d1ac4
DW
541 if (error == 0)
542 injected = true;
19852474 543 }
fd7d73c0
DW
544
545 meta.sm_type = type;
546 if (repair)
547 meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
5ef3b66a 548 error = xfrog_scrub_metadata(&ctx->mnt, &meta);
fd7d73c0
DW
549 if (!error)
550 return true;
551 switch (errno) {
552 case EROFS:
553 str_info(ctx, ctx->mntpoint,
554_("Filesystem is mounted read-only; cannot proceed."));
555 return false;
556 case ENOTRECOVERABLE:
557 str_info(ctx, ctx->mntpoint,
558_("Filesystem is mounted norecovery; cannot proceed."));
559 return false;
560 case EOPNOTSUPP:
561 case ENOTTY:
562 if (debug || verbose)
563 str_info(ctx, ctx->mntpoint,
564_("Kernel %s %s facility not detected."),
5ef3b66a 565 _(xfrog_scrubbers[type].descr),
fd7d73c0
DW
566 repair ? _("repair") : _("scrub"));
567 return false;
568 case ENOENT:
569 /* Scrubber says not present on this fs; that's fine. */
570 return true;
571 default:
572 str_info(ctx, ctx->mntpoint, "%s", strerror(errno));
573 return true;
574 }
fd7d73c0
DW
575}
576
577bool
578xfs_can_scrub_fs_metadata(
579 struct scrub_ctx *ctx)
580{
581 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, false);
582}
583
584bool
585xfs_can_scrub_inode(
586 struct scrub_ctx *ctx)
587{
588 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_INODE, false);
589}
590
591bool
592xfs_can_scrub_bmap(
593 struct scrub_ctx *ctx)
594{
595 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_BMBTD, false);
596}
597
598bool
599xfs_can_scrub_dir(
600 struct scrub_ctx *ctx)
601{
602 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_DIR, false);
603}
604
605bool
606xfs_can_scrub_attr(
607 struct scrub_ctx *ctx)
608{
609 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_XATTR, false);
610}
611
612bool
613xfs_can_scrub_symlink(
614 struct scrub_ctx *ctx)
615{
616 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_SYMLINK, false);
617}
618
619bool
620xfs_can_scrub_parent(
621 struct scrub_ctx *ctx)
622{
623 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_PARENT, false);
624}
19852474
DW
625
626bool
627xfs_can_repair(
628 struct scrub_ctx *ctx)
629{
630 return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, true);
631}
632
633/* General repair routines. */
634
635/* Repair some metadata. */
636enum check_outcome
637xfs_repair_metadata(
638 struct scrub_ctx *ctx,
639 int fd,
ee310b0c 640 struct action_item *aitem,
19852474
DW
641 unsigned int repair_flags)
642{
643 char buf[DESCR_BUFSZ];
644 struct xfs_scrub_metadata meta = { 0 };
645 struct xfs_scrub_metadata oldm;
646 int error;
647
ee310b0c 648 assert(aitem->type < XFS_SCRUB_TYPE_NR);
19852474 649 assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
ee310b0c
DW
650 meta.sm_type = aitem->type;
651 meta.sm_flags = aitem->flags | XFS_SCRUB_IFLAG_REPAIR;
5ef3b66a
DW
652 switch (xfrog_scrubbers[aitem->type].type) {
653 case XFROG_SCRUB_TYPE_AGHEADER:
654 case XFROG_SCRUB_TYPE_PERAG:
ee310b0c 655 meta.sm_agno = aitem->agno;
19852474 656 break;
5ef3b66a 657 case XFROG_SCRUB_TYPE_INODE:
ee310b0c
DW
658 meta.sm_ino = aitem->ino;
659 meta.sm_gen = aitem->gen;
19852474
DW
660 break;
661 default:
662 break;
663 }
664
ee310b0c 665 if (!is_corrupt(&meta) && (repair_flags & XRM_REPAIR_ONLY))
19852474
DW
666 return CHECK_RETRY;
667
668 memcpy(&oldm, &meta, sizeof(oldm));
5ef3b66a
DW
669 format_scrub_descr(buf, DESCR_BUFSZ, &meta,
670 &xfrog_scrubbers[meta.sm_type]);
19852474
DW
671
672 if (needs_repair(&meta))
673 str_info(ctx, buf, _("Attempting repair."));
674 else if (debug || verbose)
675 str_info(ctx, buf, _("Attempting optimization."));
676
5ef3b66a 677 error = xfrog_scrub_metadata(&ctx->mnt, &meta);
19852474
DW
678 if (error) {
679 switch (errno) {
680 case EDEADLOCK:
681 case EBUSY:
682 /* Filesystem is busy, try again later. */
683 if (debug || verbose)
684 str_info(ctx, buf,
685_("Filesystem is busy, deferring repair."));
686 return CHECK_RETRY;
687 case ESHUTDOWN:
688 /* Filesystem is already shut down, abort. */
82377bde 689 str_info(ctx, buf,
19852474
DW
690_("Filesystem is shut down, aborting."));
691 return CHECK_ABORT;
692 case ENOTTY:
693 case EOPNOTSUPP:
c3a140f0
DW
694 /*
695 * If we're in no-complain mode, requeue the check for
696 * later. It's possible that an error in another
697 * component caused us to flag an error in this
698 * component. Even if the kernel didn't think it
699 * could fix this, it's at least worth trying the scan
700 * again to see if another repair fixed it.
701 */
702 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
703 return CHECK_RETRY;
19852474 704 /*
b07c2ba5
DW
705 * If we forced repairs or this is a preen, don't
706 * error out if the kernel doesn't know how to fix.
19852474 707 */
b07c2ba5
DW
708 if (is_unoptimized(&oldm) ||
709 debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
19852474
DW
710 return CHECK_DONE;
711 /* fall through */
712 case EINVAL:
713 /* Kernel doesn't know how to repair this? */
714 str_error(ctx, buf,
715_("Don't know how to fix; offline repair required."));
716 return CHECK_DONE;
717 case EROFS:
718 /* Read-only filesystem, can't fix. */
719 if (verbose || debug || needs_repair(&oldm))
720 str_info(ctx, buf,
721_("Read-only filesystem; cannot make changes."));
722 return CHECK_DONE;
723 case ENOENT:
724 /* Metadata not present, just skip it. */
725 return CHECK_DONE;
726 case ENOMEM:
727 case ENOSPC:
728 /* Don't care if preen fails due to low resources. */
729 if (is_unoptimized(&oldm) && !needs_repair(&oldm))
730 return CHECK_DONE;
731 /* fall through */
732 default:
c3a140f0
DW
733 /*
734 * Operational error. If the caller doesn't want us
735 * to complain about repair failures, tell the caller
736 * to requeue the repair for later and don't say a
737 * thing. Otherwise, print error and bail out.
738 */
739 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
740 return CHECK_RETRY;
19852474
DW
741 str_errno(ctx, buf);
742 return CHECK_DONE;
743 }
744 }
06e49f3e 745 if (repair_flags & XRM_COMPLAIN_IF_UNFIXED)
19852474
DW
746 xfs_scrub_warn_incomplete_scrub(ctx, buf, &meta);
747 if (needs_repair(&meta)) {
c3a140f0
DW
748 /*
749 * Still broken; if we've been told not to complain then we
750 * just requeue this and try again later. Otherwise we
751 * log the error loudly and don't try again.
752 */
753 if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
754 return CHECK_RETRY;
755 str_error(ctx, buf,
19852474
DW
756_("Repair unsuccessful; offline repair required."));
757 } else {
758 /* Clean operation, no corruption detected. */
759 if (needs_repair(&oldm))
760 record_repair(ctx, buf, _("Repairs successful."));
761 else
762 record_preen(ctx, buf, _("Optimization successful."));
763 }
764 return CHECK_DONE;
765}