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