]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - scrub/scrub.c
xfsprogs: Release v6.8.0
[thirdparty/xfsprogs-dev.git] / scrub / scrub.c
index 4ef19656dcdb08210ab77e98eb26f78493c9d31e..1c53260cc26cd7b1e0fd211bdcca3dff30b6cc25 100644 (file)
@@ -1,7 +1,7 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
- * Copyright (C) 2018 Oracle.  All Rights Reserved.
- * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ * Copyright (C) 2018-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
  */
 #include "xfs.h"
 #include <stdint.h>
@@ -18,7 +18,6 @@
 #include "common.h"
 #include "progress.h"
 #include "scrub.h"
-#include "xfs_errortag.h"
 #include "repair.h"
 #include "descr.h"
 
@@ -35,21 +34,22 @@ format_scrub_descr(
        struct xfs_scrub_metadata       *meta = where;
        const struct xfrog_scrub_descr  *sc = &xfrog_scrubbers[meta->sm_type];
 
-       switch (sc->type) {
-       case XFROG_SCRUB_TYPE_AGHEADER:
-       case XFROG_SCRUB_TYPE_PERAG:
+       switch (sc->group) {
+       case XFROG_SCRUB_GROUP_AGHEADER:
+       case XFROG_SCRUB_GROUP_PERAG:
                return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
                                _(sc->descr));
                break;
-       case XFROG_SCRUB_TYPE_INODE:
+       case XFROG_SCRUB_GROUP_INODE:
                return scrub_render_ino_descr(ctx, buf, buflen,
                                meta->sm_ino, meta->sm_gen, "%s",
                                _(sc->descr));
                break;
-       case XFROG_SCRUB_TYPE_FS:
+       case XFROG_SCRUB_GROUP_FS:
+       case XFROG_SCRUB_GROUP_SUMMARY:
                return snprintf(buf, buflen, _("%s"), _(sc->descr));
                break;
-       case XFROG_SCRUB_TYPE_NONE:
+       case XFROG_SCRUB_GROUP_NONE:
                assert(0);
                break;
        }
@@ -122,6 +122,7 @@ scrub_warn_incomplete_scrub(
 static enum check_outcome
 xfs_check_metadata(
        struct scrub_ctx                *ctx,
+       struct xfs_fd                   *xfdp,
        struct xfs_scrub_metadata       *meta,
        bool                            is_inode)
 {
@@ -135,7 +136,7 @@ xfs_check_metadata(
 
        dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta->sm_flags);
 retry:
-       error = -xfrog_scrub_metadata(&ctx->mnt, meta);
+       error = -xfrog_scrub_metadata(xfdp, meta);
        if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
                meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
        switch (error) {
@@ -276,12 +277,12 @@ scrub_save_repair(
        memset(aitem, 0, sizeof(*aitem));
        aitem->type = meta->sm_type;
        aitem->flags = meta->sm_flags;
-       switch (xfrog_scrubbers[meta->sm_type].type) {
-       case XFROG_SCRUB_TYPE_AGHEADER:
-       case XFROG_SCRUB_TYPE_PERAG:
+       switch (xfrog_scrubbers[meta->sm_type].group) {
+       case XFROG_SCRUB_GROUP_AGHEADER:
+       case XFROG_SCRUB_GROUP_PERAG:
                aitem->agno = meta->sm_agno;
                break;
-       case XFROG_SCRUB_TYPE_INODE:
+       case XFROG_SCRUB_GROUP_INODE:
                aitem->ino = meta->sm_ino;
                aitem->gen = meta->sm_gen;
                break;
@@ -316,7 +317,7 @@ scrub_meta_type(
        background_sleep();
 
        /* Check the item. */
-       fix = xfs_check_metadata(ctx, &meta, false);
+       fix = xfs_check_metadata(ctx, &ctx->mnt, &meta, false);
        progress_add(1);
 
        switch (fix) {
@@ -336,14 +337,14 @@ scrub_meta_type(
 }
 
 /*
- * Scrub all metadata types that are assigned to the given XFROG_SCRUB_TYPE_*,
+ * Scrub all metadata types that are assigned to the given XFROG_SCRUB_GROUP_*,
  * saving corruption reports for later.  This should not be used for
- * XFROG_SCRUB_TYPE_INODE or for checking summary metadata.
+ * XFROG_SCRUB_GROUP_INODE or for checking summary metadata.
  */
 static bool
-scrub_all_types(
+scrub_group(
        struct scrub_ctx                *ctx,
-       enum xfrog_scrub_type           scrub_type,
+       enum xfrog_scrub_group          group,
        xfs_agnumber_t                  agno,
        struct action_list              *alist)
 {
@@ -354,9 +355,7 @@ scrub_all_types(
        for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
                int                     ret;
 
-               if (sc->type != scrub_type)
-                       continue;
-               if (sc->flags & XFROG_SCRUB_DESCR_SUMMARY)
+               if (sc->group != group)
                        continue;
 
                ret = scrub_meta_type(ctx, type, agno, alist);
@@ -388,7 +387,7 @@ scrub_ag_headers(
        xfs_agnumber_t                  agno,
        struct action_list              *alist)
 {
-       return scrub_all_types(ctx, XFROG_SCRUB_TYPE_AGHEADER, agno, alist);
+       return scrub_group(ctx, XFROG_SCRUB_GROUP_AGHEADER, agno, alist);
 }
 
 /* Scrub each AG's metadata btrees. */
@@ -398,21 +397,33 @@ scrub_ag_metadata(
        xfs_agnumber_t                  agno,
        struct action_list              *alist)
 {
-       return scrub_all_types(ctx, XFROG_SCRUB_TYPE_PERAG, agno, alist);
+       return scrub_group(ctx, XFROG_SCRUB_GROUP_PERAG, agno, alist);
 }
 
-/* Scrub whole-FS metadata btrees. */
+/* Scrub whole-filesystem metadata. */
 int
 scrub_fs_metadata(
+       struct scrub_ctx                *ctx,
+       unsigned int                    type,
+       struct action_list              *alist)
+{
+       ASSERT(xfrog_scrubbers[type].group == XFROG_SCRUB_GROUP_FS);
+
+       return scrub_meta_type(ctx, type, 0, alist);
+}
+
+/* Scrub all FS summary metadata. */
+int
+scrub_summary_metadata(
        struct scrub_ctx                *ctx,
        struct action_list              *alist)
 {
-       return scrub_all_types(ctx, XFROG_SCRUB_TYPE_FS, 0, alist);
+       return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, 0, alist);
 }
 
-/* Scrub FS summary metadata. */
+/* Scrub /only/ the superblock summary counters. */
 int
-scrub_fs_summary(
+scrub_fs_counters(
        struct scrub_ctx                *ctx,
        struct action_list              *alist)
 {
@@ -430,12 +441,12 @@ scrub_estimate_ag_work(
 
        sc = xfrog_scrubbers;
        for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
-               switch (sc->type) {
-               case XFROG_SCRUB_TYPE_AGHEADER:
-               case XFROG_SCRUB_TYPE_PERAG:
+               switch (sc->group) {
+               case XFROG_SCRUB_GROUP_AGHEADER:
+               case XFROG_SCRUB_GROUP_PERAG:
                        estimate += ctx->mnt.fsgeom.agcount;
                        break;
-               case XFROG_SCRUB_TYPE_FS:
+               case XFROG_SCRUB_GROUP_FS:
                        estimate++;
                        break;
                default:
@@ -446,29 +457,42 @@ scrub_estimate_ag_work(
 }
 
 /*
- * Scrub inode metadata.  If errors occur, this function will log them and
- * return nonzero.
+ * Scrub file metadata of some sort.  If errors occur, this function will log
+ * them and return nonzero.
  */
-static int
-__scrub_file(
+int
+scrub_file(
        struct scrub_ctx                *ctx,
-       uint64_t                        ino,
-       uint32_t                        gen,
+       int                             fd,
+       const struct xfs_bulkstat       *bstat,
        unsigned int                    type,
        struct action_list              *alist)
 {
        struct xfs_scrub_metadata       meta = {0};
+       struct xfs_fd                   xfd;
+       struct xfs_fd                   *xfdp = &ctx->mnt;
        enum check_outcome              fix;
 
        assert(type < XFS_SCRUB_TYPE_NR);
-       assert(xfrog_scrubbers[type].type == XFROG_SCRUB_TYPE_INODE);
+       assert(xfrog_scrubbers[type].group == XFROG_SCRUB_GROUP_INODE);
 
        meta.sm_type = type;
-       meta.sm_ino = ino;
-       meta.sm_gen = gen;
+       meta.sm_ino = bstat->bs_ino;
+       meta.sm_gen = bstat->bs_gen;
+
+       /*
+        * 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 (fd >= 0) {
+               memcpy(&xfd, xfdp, sizeof(xfd));
+               xfd.fd = fd;
+               xfdp = &xfd;
+       }
 
        /* Scrub the piece of metadata. */
-       fix = xfs_check_metadata(ctx, &meta, true);
+       fix = xfs_check_metadata(ctx, xfdp, &meta, true);
        if (fix == CHECK_ABORT)
                return ECANCELED;
        if (fix == CHECK_DONE)
@@ -477,86 +501,6 @@ __scrub_file(
        return scrub_save_repair(ctx, alist, &meta);
 }
 
-int
-scrub_inode_fields(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_INODE, alist);
-}
-
-int
-scrub_data_fork(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTD, alist);
-}
-
-int
-scrub_attr_fork(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTA, alist);
-}
-
-int
-scrub_cow_fork(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTC, alist);
-}
-
-int
-scrub_dir(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_DIR, alist);
-}
-
-int
-scrub_attr(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_XATTR, alist);
-}
-
-int
-scrub_symlink(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_SYMLINK, alist);
-}
-
-int
-scrub_parent(
-       struct scrub_ctx        *ctx,
-       uint64_t                ino,
-       uint32_t                gen,
-       struct action_list      *alist)
-{
-       return __scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_PARENT, alist);
-}
-
 /*
  * Test the availability of a kernel scrub command.  If errors occur (or the
  * scrub ioctl is rejected) the errors will be logged and this function will
@@ -566,26 +510,16 @@ static bool
 __scrub_test(
        struct scrub_ctx                *ctx,
        unsigned int                    type,
-       bool                            repair)
+       unsigned int                    flags)
 {
        struct xfs_scrub_metadata       meta = {0};
-       struct xfs_error_injection      inject;
-       static bool                     injected;
        int                             error;
 
        if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
                return false;
-       if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !injected) {
-               inject.fd = ctx->mnt.fd;
-               inject.errtag = XFS_ERRTAG_FORCE_SCRUB_REPAIR;
-               error = ioctl(ctx->mnt.fd, XFS_IOC_ERROR_INJECTION, &inject);
-               if (error == 0)
-                       injected = true;
-       }
 
        meta.sm_type = type;
-       if (repair)
-               meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
+       meta.sm_flags = flags;
        error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
        switch (error) {
        case 0:
@@ -598,13 +532,15 @@ _("Filesystem is mounted read-only; cannot proceed."));
                str_info(ctx, ctx->mntpoint,
 _("Filesystem is mounted norecovery; cannot proceed."));
                return false;
+       case EINVAL:
        case EOPNOTSUPP:
        case ENOTTY:
                if (debug || verbose)
                        str_info(ctx, ctx->mntpoint,
 _("Kernel %s %s facility not detected."),
                                        _(xfrog_scrubbers[type].descr),
-                                       repair ? _("repair") : _("scrub"));
+                                       (flags & XFS_SCRUB_IFLAG_REPAIR) ?
+                                               _("repair") : _("scrub"));
                return false;
        case ENOENT:
                /* Scrubber says not present on this fs; that's fine. */
@@ -619,56 +555,64 @@ bool
 can_scrub_fs_metadata(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, false);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, 0);
 }
 
 bool
 can_scrub_inode(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_INODE, false);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_INODE, 0);
 }
 
 bool
 can_scrub_bmap(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_BMBTD, false);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_BMBTD, 0);
 }
 
 bool
 can_scrub_dir(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_DIR, false);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_DIR, 0);
 }
 
 bool
 can_scrub_attr(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_XATTR, false);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_XATTR, 0);
 }
 
 bool
 can_scrub_symlink(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_SYMLINK, false);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_SYMLINK, 0);
 }
 
 bool
 can_scrub_parent(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_PARENT, false);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_PARENT, 0);
 }
 
 bool
 xfs_can_repair(
        struct scrub_ctx        *ctx)
 {
-       return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, true);
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, XFS_SCRUB_IFLAG_REPAIR);
+}
+
+bool
+can_force_rebuild(
+       struct scrub_ctx        *ctx)
+{
+       return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE,
+                       XFS_SCRUB_IFLAG_REPAIR | XFS_SCRUB_IFLAG_FORCE_REBUILD);
 }
 
 /* General repair routines. */
@@ -677,7 +621,7 @@ xfs_can_repair(
 enum check_outcome
 xfs_repair_metadata(
        struct scrub_ctx                *ctx,
-       int                             fd,
+       struct xfs_fd                   *xfdp,
        struct action_item              *aitem,
        unsigned int                    repair_flags)
 {
@@ -690,12 +634,14 @@ xfs_repair_metadata(
        assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
        meta.sm_type = aitem->type;
        meta.sm_flags = aitem->flags | XFS_SCRUB_IFLAG_REPAIR;
-       switch (xfrog_scrubbers[aitem->type].type) {
-       case XFROG_SCRUB_TYPE_AGHEADER:
-       case XFROG_SCRUB_TYPE_PERAG:
+       if (use_force_rebuild)
+               meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
+       switch (xfrog_scrubbers[aitem->type].group) {
+       case XFROG_SCRUB_GROUP_AGHEADER:
+       case XFROG_SCRUB_GROUP_PERAG:
                meta.sm_agno = aitem->agno;
                break;
-       case XFROG_SCRUB_TYPE_INODE:
+       case XFROG_SCRUB_GROUP_INODE:
                meta.sm_ino = aitem->ino;
                meta.sm_gen = aitem->gen;
                break;
@@ -715,7 +661,7 @@ xfs_repair_metadata(
                str_info(ctx, descr_render(&dsc),
                                _("Attempting optimization."));
 
-       error = -xfrog_scrub_metadata(&ctx->mnt, &meta);
+       error = -xfrog_scrub_metadata(xfdp, &meta);
        switch (error) {
        case 0:
                /* No operational errors encountered. */
@@ -734,6 +680,15 @@ _("Filesystem is shut down, aborting."));
                return CHECK_ABORT;
        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"))
+                       return CHECK_DONE;
                /*
                 * If we're in no-complain mode, requeue the check for
                 * later.  It's possible that an error in another
@@ -744,13 +699,6 @@ _("Filesystem is shut down, aborting."));
                 */
                if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
                        return CHECK_RETRY;
-               /*
-                * If we forced repairs or this is a preen, don't
-                * error out if the kernel doesn't know how to fix.
-                */
-               if (is_unoptimized(&oldm) ||
-                   debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
-                       return CHECK_DONE;
                fallthrough;
        case EINVAL:
                /* Kernel doesn't know how to repair this? */