1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#include "xfs.h"
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include "list.h"
#include "libfrog/paths.h"
#include "libfrog/fsgeom.h"
#include "libfrog/scrub.h"
#include "xfs_scrub.h"
#include "common.h"
#include "scrub.h"
#include "progress.h"
#include "repair.h"
#include "descr.h"
#include "scrub_private.h"
/* General repair routines. */
/*
* Bitmap showing the correctness dependencies between scrub types for repairs.
* There are no edges between AG btrees and AG headers because we can't mount
* the filesystem if the btree root pointers in the AG headers are wrong.
* Dependencies cannot cross scrub groups.
*/
#define DEP(x) (1U << (x))
static const unsigned int repair_deps[XFS_SCRUB_TYPE_NR] = {
[XFS_SCRUB_TYPE_BMBTD] = DEP(XFS_SCRUB_TYPE_INODE),
[XFS_SCRUB_TYPE_BMBTA] = DEP(XFS_SCRUB_TYPE_INODE),
[XFS_SCRUB_TYPE_BMBTC] = DEP(XFS_SCRUB_TYPE_INODE),
[XFS_SCRUB_TYPE_DIR] = DEP(XFS_SCRUB_TYPE_BMBTD),
[XFS_SCRUB_TYPE_XATTR] = DEP(XFS_SCRUB_TYPE_BMBTA),
[XFS_SCRUB_TYPE_SYMLINK] = DEP(XFS_SCRUB_TYPE_BMBTD),
[XFS_SCRUB_TYPE_PARENT] = DEP(XFS_SCRUB_TYPE_DIR) |
DEP(XFS_SCRUB_TYPE_XATTR),
[XFS_SCRUB_TYPE_QUOTACHECK] = DEP(XFS_SCRUB_TYPE_UQUOTA) |
DEP(XFS_SCRUB_TYPE_GQUOTA) |
DEP(XFS_SCRUB_TYPE_PQUOTA),
[XFS_SCRUB_TYPE_RTSUM] = DEP(XFS_SCRUB_TYPE_RTBITMAP),
};
#undef DEP
/*
* Decide if we want an automatic downgrade to dry-run mode. This is only
* for service mode, where we are fed a path and have to figure out if the fs
* is repairable or not.
*/
bool
repair_want_service_downgrade(
struct scrub_ctx *ctx)
{
struct xfs_scrub_metadata meta = {
.sm_type = XFS_SCRUB_TYPE_PROBE,
.sm_flags = XFS_SCRUB_IFLAG_REPAIR,
};
int error;
if (ctx->mode == SCRUB_MODE_DRY_RUN)
return false;
if (!is_service)
return false;
if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
return false;
error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
switch (error) {
case EROFS:
case ENOTRECOVERABLE:
case EOPNOTSUPP:
return true;
}
return false;
}
static inline void
restore_oldvec(
struct xfs_scrub_vec *oldvec,
const struct scrub_item *sri,
unsigned int scrub_type)
{
oldvec->sv_type = scrub_type;
oldvec->sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
}
static int
repair_epilogue(
struct scrub_ctx *ctx,
struct descr *dsc,
struct scrub_item *sri,
unsigned int repair_flags,
const struct xfs_scrub_vec *meta)
{
struct xfs_scrub_vec oldv;
struct xfs_scrub_vec *oldm = &oldv;
unsigned int scrub_type = meta->sv_type;
int error = -meta->sv_ret;
restore_oldvec(oldm, sri, meta->sv_type);
switch (error) {
case 0:
/* No operational errors encountered. */
break;
case EDEADLOCK:
case EBUSY:
/* Filesystem is busy, try again later. */
if (debug || verbose)
str_info(ctx, descr_render(dsc),
_("Filesystem is busy, deferring repair."));
return 0;
case ESHUTDOWN:
/* Filesystem is already shut down, abort. */
str_error(ctx, descr_render(dsc),
_("Filesystem is shut down, aborting."));
return ECANCELED;
case ENOTTY:
case EOPNOTSUPP:
/*
* If the kernel cannot perform the optimization that we
* requested; or we forced a repair but the kernel doesn't know
* how to perform the repair, don't requeue the request. Mark
* it done and move on.
*/
if (is_unoptimized(oldm) ||
debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) {
scrub_item_clean_state(sri, scrub_type);
return 0;
}
/*
* If we're in no-complain mode, requeue the check for
* later. It's possible that an error in another
* component caused us to flag an error in this
* component. Even if the kernel didn't think it
* could fix this, it's at least worth trying the scan
* again to see if another repair fixed it.
*/
if (!(repair_flags & XRM_FINAL_WARNING))
return 0;
fallthrough;
case EINVAL:
/* Kernel doesn't know how to repair this? */
str_corrupt(ctx, descr_render(dsc),
_("Don't know how to fix; offline repair required."));
scrub_item_clean_state(sri, scrub_type);
return 0;
case EROFS:
/* Read-only filesystem, can't fix. */
if (verbose || debug || needs_repair(oldm))
str_error(ctx, descr_render(dsc),
_("Read-only filesystem; cannot make changes."));
return ECANCELED;
case ENOENT:
/* Metadata not present, just skip it. */
scrub_item_clean_state(sri, scrub_type);
return 0;
case ENOMEM:
case ENOSPC:
/* Don't care if preen fails due to low resources. */
if (is_unoptimized(oldm) && !needs_repair(oldm)) {
scrub_item_clean_state(sri, scrub_type);
return 0;
}
fallthrough;
default:
/*
* Operational error. If the caller doesn't want us to
* complain about repair failures, tell the caller to requeue
* the repair for later and don't say a thing. Otherwise,
* print an error, mark the item clean because we're done with
* trying to repair it, and bail out.
*/
if (!(repair_flags & XRM_FINAL_WARNING))
return 0;
str_liberror(ctx, error, descr_render(dsc));
scrub_item_clean_state(sri, scrub_type);
return 0;
}
/*
* If the kernel says the repair was incomplete or that there was a
* cross-referencing discrepancy but no obvious corruption, we'll try
* the repair again, just in case the fs was busy. Only retry so many
* times.
*/
if (want_retry(meta) && scrub_item_schedule_retry(sri, scrub_type))
return 0;
if (repair_flags & XRM_FINAL_WARNING)
scrub_warn_incomplete_scrub(ctx, dsc, meta);
if (needs_repair(meta) || is_incomplete(meta)) {
/*
* Still broken; if we've been told not to complain then we
* just requeue this and try again later. Otherwise we
* log the error loudly and don't try again.
*/
if (!(repair_flags & XRM_FINAL_WARNING))
return 0;
str_corrupt(ctx, descr_render(dsc),
_("Repair unsuccessful; offline repair required."));
} else if (xref_failed(meta)) {
/*
* This metadata object itself looks ok, but we still noticed
* inconsistencies when comparing it with the other filesystem
* metadata. If we're in "final warning" mode, advise the
* caller to run xfs_repair; otherwise, we'll keep trying to
* reverify the cross-referencing as repairs progress.
*/
if (repair_flags & XRM_FINAL_WARNING) {
str_info(ctx, descr_render(dsc),
_("Seems correct but cross-referencing failed; offline repair recommended."));
} else {
if (verbose)
str_info(ctx, descr_render(dsc),
_("Seems correct but cross-referencing failed; will keep checking."));
return 0;
}
} else if (meta->sv_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) {
if (verbose)
str_info(ctx, descr_render(dsc),
_("No modification needed."));
} else {
/* Clean operation, no corruption detected. */
if (is_corrupt(oldm))
record_repair(ctx, descr_render(dsc),
_("Repairs successful."));
else if (xref_disagrees(oldm))
record_repair(ctx, descr_render(dsc),
_("Repairs successful after discrepancy in cross-referencing."));
else if (xref_failed(oldm))
record_repair(ctx, descr_render(dsc),
_("Repairs successful after cross-referencing failure."));
else
record_preen(ctx, descr_render(dsc),
_("Optimization successful."));
}
scrub_item_clean_state(sri, scrub_type);
return 0;
}
/* Decide if the dependent scrub types of the given scrub type are ok. */
static bool
repair_item_dependencies_ok(
const struct scrub_item *sri,
unsigned int scrub_type)
{
unsigned int dep_mask = repair_deps[scrub_type];
unsigned int b;
for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
if (!(dep_mask & 1))
continue;
/*
* If this lower level object also needs repair, we can't fix
* the higher level item.
*/
if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
return false;
}
return true;
}
/* Decide if we want to repair a particular type of metadata. */
static bool
can_repair_now(
const struct scrub_item *sri,
unsigned int scrub_type,
__u32 repair_mask,
unsigned int repair_flags)
{
struct xfs_scrub_vec oldvec;
bool repair_only;
/* Do we even need to repair this thing? */
if (!(sri->sri_state[scrub_type] & repair_mask))
return false;
restore_oldvec(&oldvec, sri, scrub_type);
/*
* If the caller boosted the priority of this scrub type on behalf of a
* higher level repair by setting IFLAG_REPAIR, ignore REPAIR_ONLY.
*/
repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
!(sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR);
if (!is_corrupt(&oldvec) && repair_only)
return false;
/*
* Don't try to repair higher level items if their lower-level
* dependencies haven't been verified, unless this is our last chance
* to fix things without complaint.
*/
if (!(repair_flags & XRM_FINAL_WARNING) &&
!repair_item_dependencies_ok(sri, scrub_type))
return false;
return true;
}
/*
* Repair some metadata.
*
* Returns 0 for success (or repair item deferral), or ECANCELED to abort the
* program.
*/
static int
repair_call_kernel(
struct scrub_ctx *ctx,
struct xfs_fd *xfdp,
struct scrub_item *sri,
__u32 repair_mask,
unsigned int repair_flags)
{
DEFINE_DESCR(dsc, ctx, format_scrubv_descr);
struct xfrog_scrubv scrubv = { };
struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv);
struct xfs_scrub_vec *v;
unsigned int scrub_type;
bool need_barrier = false;
int error;
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
xfrog_scrubv_from_item(&scrubv, sri);
descr_set(&dsc, &vdesc);
foreach_scrub_type(scrub_type) {
if (scrub_excessive_errors(ctx))
return ECANCELED;
if (!can_repair_now(sri, scrub_type, repair_mask,
repair_flags))
continue;
if (need_barrier) {
xfrog_scrubv_add_barrier(&scrubv);
need_barrier = false;
}
xfrog_scrubv_add_item(&scrubv, sri, scrub_type, true);
if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR)
str_info(ctx, descr_render(&dsc),
_("Attempting repair."));
else if (debug || verbose)
str_info(ctx, descr_render(&dsc),
_("Attempting optimization."));
dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc),
sri->sri_state[scrub_type],
sri->sri_tries[scrub_type]);
/*
* One of the other scrub types depends on this one. Set us up
* to add a repair barrier if we decide to schedule a repair
* after this one. If the UNFIXED flag is set, that means this
* is our last chance to fix things, so we skip the barriers
* just let everything run.
*/
if (!(repair_flags & XRM_FINAL_WARNING) &&
(sri->sri_state[scrub_type] & SCRUB_ITEM_BARRIER))
need_barrier = true;
}
error = -xfrog_scrubv_metadata(xfdp, &scrubv);
if (error)
return error;
foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
/* Deal with barriers separately. */
if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) {
/* -ECANCELED means the kernel stopped here. */
if (v->sv_ret == -ECANCELED)
return 0;
if (v->sv_ret)
return -v->sv_ret;
continue;
}
error = repair_epilogue(ctx, &dsc, sri, repair_flags, v);
if (error)
return error;
/* Maybe update progress if we fixed the problem. */
if (!(repair_flags & XRM_NOPROGRESS) &&
!(sri->sri_state[v->sv_type] & SCRUB_ITEM_REPAIR_ANY))
progress_add(1);
}
return 0;
}
/*
* Prioritize action items in order of how long we can wait.
*
* To minimize the amount of repair work, we want to prioritize metadata
* objects by perceived corruptness. If CORRUPT is set, the fields are
* just plain bad; try fixing that first. Otherwise if XCORRUPT is set,
* the fields could be bad, but the xref data could also be bad; we'll
* try fixing that next. Finally, if XFAIL is set, some other metadata
* structure failed validation during xref, so we'll recheck this
* metadata last since it was probably fine.
*
* For metadata that lie in the critical path of checking other metadata
* (superblock, AG{F,I,FL}, inobt) we scrub and fix those things before
* we even get to handling their dependencies, so things should progress
* in order.
*/
struct action_item {
struct list_head list;
struct scrub_item sri;
};
/*
* The operation of higher level metadata objects depends on the correctness of
* lower level metadata objects. This means that if X depends on Y, we must
* investigate and correct all the observed issues with Y before we try to make
* a correction to X. For all scheduled repair activity on X, boost the
* priority of repairs on all the Ys to ensure this correctness.
*/
static void
repair_item_boost_priorities(
struct scrub_item *sri)
{
unsigned int scrub_type;
foreach_scrub_type(scrub_type) {
unsigned int dep_mask = repair_deps[scrub_type];
unsigned int b;
if (repair_item_count_needsrepair(sri) == 0 || !dep_mask)
continue;
/*
* Check if the repairs for this scrub type depend on any other
* scrub types that have been flagged with cross-referencing
* errors and are not already tagged for the highest priority
* repair (SCRUB_ITEM_CORRUPT). If so, boost the priority of
* that scrub type (via SCRUB_ITEM_BOOST_REPAIR) so that any
* problems with the dependencies will (hopefully) be fixed
* before we start repairs on this scrub type.
*
* So far in the history of xfs_scrub we have maintained that
* lower numbered scrub types do not depend on higher numbered
* scrub types, so we need only process the bit mask once.
*/
for (b = 0; b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
if (!dep_mask)
break;
if (!(dep_mask & 1))
continue;
if (!(sri->sri_state[b] & SCRUB_ITEM_REPAIR_XREF))
continue;
if (sri->sri_state[b] & SCRUB_ITEM_CORRUPT)
continue;
sri->sri_state[b] |= SCRUB_ITEM_BOOST_REPAIR;
}
}
}
/*
* These are the scrub item state bits that must be copied when scheduling
* a (per-AG) scrub type for immediate repairs. The original state tracking
* bits are left untouched to force a rescan in phase 4.
*/
#define MUSTFIX_STATES (SCRUB_ITEM_CORRUPT | \
SCRUB_ITEM_BOOST_REPAIR | \
SCRUB_ITEM_BARRIER)
/*
* Figure out which AG metadata must be fixed before we can move on
* to the inode scan.
*/
void
repair_item_mustfix(
struct scrub_item *sri,
struct scrub_item *fix_now)
{
unsigned int scrub_type;
assert(sri->sri_agno != -1U);
repair_item_boost_priorities(sri);
scrub_item_init_ag(fix_now, sri->sri_agno);
foreach_scrub_type(scrub_type) {
unsigned int state;
state = sri->sri_state[scrub_type] & MUSTFIX_STATES;
if (!state)
continue;
switch (scrub_type) {
case XFS_SCRUB_TYPE_AGI:
case XFS_SCRUB_TYPE_FINOBT:
case XFS_SCRUB_TYPE_INOBT:
fix_now->sri_state[scrub_type] = state;
break;
}
}
}
/*
* These scrub item states correspond to metadata that is inconsistent in some
* way and must be repaired. If too many metadata objects share these states,
* this can make repairs difficult.
*/
#define HARDREPAIR_STATES (SCRUB_ITEM_CORRUPT | \
SCRUB_ITEM_XCORRUPT | \
SCRUB_ITEM_XFAIL)
/* Determine if primary or secondary metadata are inconsistent. */
unsigned int
repair_item_difficulty(
const struct scrub_item *sri)
{
unsigned int scrub_type;
unsigned int ret = 0;
foreach_scrub_type(scrub_type) {
unsigned int state;
state = sri->sri_state[scrub_type] & HARDREPAIR_STATES;
if (!state)
continue;
switch (scrub_type) {
case XFS_SCRUB_TYPE_RMAPBT:
case XFS_SCRUB_TYPE_RTRMAPBT:
ret |= REPAIR_DIFFICULTY_SECONDARY;
break;
case XFS_SCRUB_TYPE_SB:
case XFS_SCRUB_TYPE_AGF:
case XFS_SCRUB_TYPE_AGFL:
case XFS_SCRUB_TYPE_AGI:
case XFS_SCRUB_TYPE_FINOBT:
case XFS_SCRUB_TYPE_INOBT:
case XFS_SCRUB_TYPE_BNOBT:
case XFS_SCRUB_TYPE_CNTBT:
case XFS_SCRUB_TYPE_REFCNTBT:
case XFS_SCRUB_TYPE_RTBITMAP:
case XFS_SCRUB_TYPE_RTSUM:
case XFS_SCRUB_TYPE_RGSUPER:
case XFS_SCRUB_TYPE_RTREFCBT:
ret |= REPAIR_DIFFICULTY_PRIMARY;
break;
}
}
return ret;
}
/* Create a new repair action list. */
int
action_list_alloc(
struct action_list **listp)
{
struct action_list *alist;
alist = malloc(sizeof(struct action_list));
if (!alist)
return errno;
action_list_init(alist);
*listp = alist;
return 0;
}
/* Free the repair lists. */
void
action_list_free(
struct action_list **listp)
{
struct action_list *alist = *listp;
struct action_item *aitem;
struct action_item *n;
if (!(*listp))
return;
list_for_each_entry_safe(aitem, n, &alist->list, list) {
list_del(&aitem->list);
free(aitem);
}
free(alist);
*listp = NULL;
}
/* Number of pending repairs in this list. */
unsigned long long
action_list_length(
struct action_list *alist)
{
struct action_item *aitem;
unsigned long long ret = 0;
list_for_each_entry(aitem, &alist->list, list)
ret += repair_item_count_needsrepair(&aitem->sri);
return ret;
}
/* Remove the first action item from the action list. */
struct action_item *
action_list_pop(
struct action_list *alist)
{
struct action_item *aitem;
aitem = list_first_entry_or_null(&alist->list, struct action_item,
list);
if (!aitem)
return NULL;
list_del_init(&aitem->list);
return aitem;
}
/* Add an action item to the end of a list. */
void
action_list_add(
struct action_list *alist,
struct action_item *aitem)
{
list_add_tail(&aitem->list, &alist->list);
}
/*
* Try to repair a filesystem object and let the caller know what it should do
* with the action item. The caller must be able to requeue action items, so
* we don't complain if repairs are not totally successful.
*/
int
action_item_try_repair(
struct scrub_ctx *ctx,
struct action_item *aitem,
enum tryrepair_outcome *outcome)
{
struct scrub_item *sri = &aitem->sri;
unsigned int before, after;
unsigned int scrub_type;
int ret;
BUILD_BUG_ON(sizeof(sri->sri_selected) * NBBY < XFS_SCRUB_TYPE_NR);
before = repair_item_count_needsrepair(sri);
ret = repair_item(ctx, sri, 0);
if (ret)
return ret;
after = repair_item_count_needsrepair(sri);
if (after > 0) {
/*
* The kernel did not complete all of the repairs requested.
* If it made some progress we'll requeue; otherwise, let the
* caller know that nothing got fixed.
*/
if (before != after)
*outcome = TR_REQUEUE;
else
*outcome = TR_NOPROGRESS;
return 0;
}
/*
* Nothing in this fs object was marked inconsistent. This means we
* were merely optimizing metadata and there is no revalidation work to
* be done.
*/
if (!sri->sri_inconsistent) {
*outcome = TR_REPAIRED;
return 0;
}
/*
* We fixed inconsistent metadata, so reschedule the entire object for
* immediate revalidation to see if anything else went wrong.
*/
foreach_scrub_type(scrub_type)
if (sri->sri_selected & (1ULL << scrub_type))
sri->sri_state[scrub_type] = SCRUB_ITEM_NEEDSCHECK;
sri->sri_inconsistent = false;
sri->sri_revalidate = true;
ret = scrub_item_check(ctx, sri);
if (ret)
return ret;
after = repair_item_count_needsrepair(sri);
if (after > 0) {
/*
* Uhoh, we found something else broken. Tell the caller that
* this item needs to be queued for more repairs.
*/
sri->sri_revalidate = false;
*outcome = TR_REQUEUE;
return 0;
}
/* Repairs complete. */
*outcome = TR_REPAIRED;
return 0;
}
/* Repair everything on this list. */
int
action_list_process(
struct scrub_ctx *ctx,
struct action_list *alist,
unsigned int repair_flags)
{
struct action_item *aitem;
struct action_item *n;
int ret;
list_for_each_entry_safe(aitem, n, &alist->list, list) {
if (scrub_excessive_errors(ctx))
return ECANCELED;
ret = repair_item(ctx, &aitem->sri, repair_flags);
if (ret)
break;
if (repair_item_count_needsrepair(&aitem->sri) == 0) {
list_del(&aitem->list);
free(aitem);
}
}
return ret;
}
/*
* For a given filesystem object, perform all repairs of a given class
* (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed.
*/
static int
repair_item_class(
struct scrub_ctx *ctx,
struct scrub_item *sri,
int override_fd,
uint8_t repair_mask,
unsigned int flags)
{
struct xfs_fd xfd;
struct scrub_item old_sri;
struct xfs_fd *xfdp = &ctx->mnt;
int error = 0;
if (ctx->mode == SCRUB_MODE_DRY_RUN)
return 0;
if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN))
return 0;
if (!scrub_item_schedule_work(sri, repair_mask, repair_deps))
return 0;
/*
* If the caller passed us a file descriptor for a scrub, use it
* instead of scrub-by-handle because this enables the kernel to skip
* costly inode btree lookups.
*/
if (override_fd >= 0) {
memcpy(&xfd, xfdp, sizeof(xfd));
xfd.fd = override_fd;
xfdp = &xfd;
}
do {
memcpy(&old_sri, sri, sizeof(struct scrub_item));
error = repair_call_kernel(ctx, xfdp, sri, repair_mask, flags);
if (error)
return error;
} while (scrub_item_call_kernel_again(sri, repair_mask, &old_sri));
return 0;
}
/*
* Repair all parts (i.e. scrub types) of this filesystem object for which
* corruption has been observed directly. Other types of repair work (fixing
* cross referencing problems and preening) are deferred.
*
* This function should only be called to perform spot repairs of fs objects
* during phase 2 and 3 while we still have open handles to those objects.
*/
int
repair_item_corruption(
struct scrub_ctx *ctx,
struct scrub_item *sri)
{
return repair_item_class(ctx, sri, -1, SCRUB_ITEM_CORRUPT,
XRM_REPAIR_ONLY | XRM_NOPROGRESS);
}
/* Repair all parts of this file, similar to repair_item_corruption. */
int
repair_file_corruption(
struct scrub_ctx *ctx,
struct scrub_item *sri,
int override_fd)
{
repair_item_boost_priorities(sri);
return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT,
XRM_REPAIR_ONLY | XRM_NOPROGRESS);
}
/* Repair all parts of this file or complain if we cannot. */
int
repair_file_corruption_now(
struct scrub_ctx *ctx,
struct scrub_item *sri,
int override_fd)
{
repair_item_boost_priorities(sri);
return repair_item_class(ctx, sri, override_fd, SCRUB_ITEM_CORRUPT,
XRM_REPAIR_ONLY | XRM_NOPROGRESS | XRM_FINAL_WARNING);
}
/*
* Repair everything in this filesystem object that needs it. This includes
* cross-referencing and preening.
*/
int
repair_item(
struct scrub_ctx *ctx,
struct scrub_item *sri,
unsigned int flags)
{
int ret;
repair_item_boost_priorities(sri);
ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_CORRUPT, flags);
if (ret)
return ret;
ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_XCORRUPT, flags);
if (ret)
return ret;
ret = repair_item_class(ctx, sri, -1, SCRUB_ITEM_XFAIL, flags);
if (ret)
return ret;
return repair_item_class(ctx, sri, -1, SCRUB_ITEM_PREEN, flags);
}
/* Create an action item around a scrub item that needs repairs. */
int
repair_item_to_action_item(
struct scrub_ctx *ctx,
const struct scrub_item *sri,
struct action_item **aitemp)
{
struct action_item *aitem;
unsigned int scrub_type;
if (repair_item_count_needsrepair(sri) == 0)
return 0;
aitem = malloc(sizeof(struct action_item));
if (!aitem) {
int error = errno;
str_liberror(ctx, error, _("creating repair action item"));
return error;
}
INIT_LIST_HEAD(&aitem->list);
memcpy(&aitem->sri, sri, sizeof(struct scrub_item));
/*
* If the scrub item indicates that there is unchecked metadata, assume
* that the scrub type checker depends on something that couldn't be
* fixed. Mark that type as corrupt so that phase 4 will try it again.
*/
foreach_scrub_type(scrub_type) {
__u8 *state = aitem->sri.sri_state;
if (state[scrub_type] & SCRUB_ITEM_NEEDSCHECK) {
state[scrub_type] &= ~SCRUB_ITEM_NEEDSCHECK;
state[scrub_type] |= SCRUB_ITEM_CORRUPT;
}
}
*aitemp = aitem;
return 0;
}
|