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