]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_scrub: wrap the scrub ioctl
authorDarrick J. Wong <darrick.wong@oracle.com>
Fri, 2 Feb 2018 15:32:46 +0000 (09:32 -0600)
committerEric Sandeen <sandeen@redhat.com>
Fri, 2 Feb 2018 15:32:46 +0000 (09:32 -0600)
Create some wrappers to call the scrub ioctls.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
scrub/Makefile
scrub/common.c
scrub/common.h
scrub/phase1.c
scrub/scrub.c [new file with mode: 0644]
scrub/scrub.h [new file with mode: 0644]

index 5397339dced6d48cfa707077990ef17ede068b29..915b801f4e5eb49d8c1c32066ebf4e9d5088d7d1 100644 (file)
@@ -21,6 +21,7 @@ disk.h \
 filemap.h \
 fscounters.h \
 inodes.h \
+scrub.h \
 spacemap.h \
 xfs_scrub.h
 
@@ -31,6 +32,7 @@ filemap.c \
 fscounters.c \
 inodes.c \
 phase1.c \
+scrub.c \
 spacemap.c \
 xfs_scrub.c
 
index e9ebb040f924ab4e94caa81a7ccd1fd4309d39c0..81869e48bf78e7a0ef19f7869b5332413318c2cf 100644 (file)
@@ -266,3 +266,22 @@ find_mountpoint(
        platform_mntent_close(&cursor);
        return found;
 }
+
+/*
+ * Sleep for 100ms * however many -b we got past the initial one.
+ * This is an (albeit clumsy) way to throttle scrub activity.
+ */
+void
+background_sleep(void)
+{
+       unsigned long long      time;
+       struct timespec         tv;
+
+       if (bg_mode < 2)
+               return;
+
+       time = 100000ULL * (bg_mode - 1);
+       tv.tv_sec = time / 1000000;
+       tv.tv_nsec = time % 1000000;
+       nanosleep(&tv, NULL);
+}
index 3af63fbbb35a06c2118fa419e6dc42b546235342..96fc8708f95b3273720ae6db8e955c06b2221fb6 100644 (file)
@@ -73,5 +73,6 @@ static inline int syncfs(int fd)
 #endif
 
 bool find_mountpoint(char *mtab, struct scrub_ctx *ctx);
+void background_sleep(void);
 
 #endif /* XFS_SCRUB_COMMON_H_ */
index cb25754df8fae0ae8a544c7af4e097b31c90ebfe..6b986dd2780d16e56276009ea4d7220666378b15 100644 (file)
@@ -46,6 +46,7 @@
 #include "xfs_scrub.h"
 #include "common.h"
 #include "disk.h"
+#include "scrub.h"
 
 /* Phase 1: Find filesystem geometry (and clean up after) */
 
@@ -168,6 +169,16 @@ _("Does not appear to be an XFS filesystem!"));
                return false;
        }
 
+       /* Do we have kernel-assisted metadata scrubbing? */
+       if (!xfs_can_scrub_fs_metadata(ctx) || !xfs_can_scrub_inode(ctx) ||
+           !xfs_can_scrub_bmap(ctx) || !xfs_can_scrub_dir(ctx) ||
+           !xfs_can_scrub_attr(ctx) || !xfs_can_scrub_symlink(ctx) ||
+           !xfs_can_scrub_parent(ctx)) {
+               str_error(ctx, ctx->mntpoint,
+_("Kernel metadata scrubbing facility is required."));
+               return false;
+       }
+
        /* Go find the XFS devices if we have a usable fsmap. */
        fs_table_initialise(0, NULL, 0, NULL);
        errno = 0;
diff --git a/scrub/scrub.c b/scrub/scrub.c
new file mode 100644 (file)
index 0000000..93a94da
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2018 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "path.h"
+#include "xfs_scrub.h"
+#include "common.h"
+#include "scrub.h"
+#include "xfs_errortag.h"
+
+/* Online scrub and repair wrappers. */
+
+/* Type info and names for the scrub types. */
+enum scrub_type {
+       ST_NONE,        /* disabled */
+       ST_AGHEADER,    /* per-AG header */
+       ST_PERAG,       /* per-AG metadata */
+       ST_FS,          /* per-FS metadata */
+       ST_INODE,       /* per-inode metadata */
+};
+struct scrub_descr {
+       const char      *name;
+       enum scrub_type type;
+};
+
+/* These must correspond to XFS_SCRUB_TYPE_ */
+static const struct scrub_descr scrubbers[XFS_SCRUB_TYPE_NR] = {
+       [XFS_SCRUB_TYPE_PROBE] =
+               {"metadata",                            ST_NONE},
+       [XFS_SCRUB_TYPE_SB] =
+               {"superblock",                          ST_AGHEADER},
+       [XFS_SCRUB_TYPE_AGF] =
+               {"free space header",                   ST_AGHEADER},
+       [XFS_SCRUB_TYPE_AGFL] =
+               {"free list",                           ST_AGHEADER},
+       [XFS_SCRUB_TYPE_AGI] =
+               {"inode header",                        ST_AGHEADER},
+       [XFS_SCRUB_TYPE_BNOBT] =
+               {"freesp by block btree",               ST_PERAG},
+       [XFS_SCRUB_TYPE_CNTBT] =
+               {"freesp by length btree",              ST_PERAG},
+       [XFS_SCRUB_TYPE_INOBT] =
+               {"inode btree",                         ST_PERAG},
+       [XFS_SCRUB_TYPE_FINOBT] =
+               {"free inode btree",                    ST_PERAG},
+       [XFS_SCRUB_TYPE_RMAPBT] =
+               {"reverse mapping btree",               ST_PERAG},
+       [XFS_SCRUB_TYPE_REFCNTBT] =
+               {"reference count btree",               ST_PERAG},
+       [XFS_SCRUB_TYPE_INODE] =
+               {"inode record",                        ST_INODE},
+       [XFS_SCRUB_TYPE_BMBTD] =
+               {"data block map",                      ST_INODE},
+       [XFS_SCRUB_TYPE_BMBTA] =
+               {"attr block map",                      ST_INODE},
+       [XFS_SCRUB_TYPE_BMBTC] =
+               {"CoW block map",                       ST_INODE},
+       [XFS_SCRUB_TYPE_DIR] =
+               {"directory entries",                   ST_INODE},
+       [XFS_SCRUB_TYPE_XATTR] =
+               {"extended attributes",                 ST_INODE},
+       [XFS_SCRUB_TYPE_SYMLINK] =
+               {"symbolic link",                       ST_INODE},
+       [XFS_SCRUB_TYPE_PARENT] =
+               {"parent pointer",                      ST_INODE},
+       [XFS_SCRUB_TYPE_RTBITMAP] =
+               {"realtime bitmap",                     ST_FS},
+       [XFS_SCRUB_TYPE_RTSUM] =
+               {"realtime summary",                    ST_FS},
+       [XFS_SCRUB_TYPE_UQUOTA] =
+               {"user quotas",                         ST_FS},
+       [XFS_SCRUB_TYPE_GQUOTA] =
+               {"group quotas",                        ST_FS},
+       [XFS_SCRUB_TYPE_PQUOTA] =
+               {"project quotas",                      ST_FS},
+};
+
+/* Format a scrub description. */
+static void
+format_scrub_descr(
+       char                            *buf,
+       size_t                          buflen,
+       struct xfs_scrub_metadata       *meta,
+       const struct scrub_descr        *sc)
+{
+       switch (sc->type) {
+       case ST_AGHEADER:
+       case ST_PERAG:
+               snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
+                               _(sc->name));
+               break;
+       case ST_INODE:
+               snprintf(buf, buflen, _("Inode %"PRIu64" %s"),
+                               (uint64_t)meta->sm_ino, _(sc->name));
+               break;
+       case ST_FS:
+               snprintf(buf, buflen, _("%s"), _(sc->name));
+               break;
+       case ST_NONE:
+               assert(0);
+               break;
+       }
+}
+
+/* Predicates for scrub flag state. */
+
+static inline bool is_corrupt(struct xfs_scrub_metadata *sm)
+{
+       return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT;
+}
+
+static inline bool is_unoptimized(struct xfs_scrub_metadata *sm)
+{
+       return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN;
+}
+
+static inline bool xref_failed(struct xfs_scrub_metadata *sm)
+{
+       return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL;
+}
+
+static inline bool xref_disagrees(struct xfs_scrub_metadata *sm)
+{
+       return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT;
+}
+
+static inline bool is_incomplete(struct xfs_scrub_metadata *sm)
+{
+       return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE;
+}
+
+static inline bool is_suspicious(struct xfs_scrub_metadata *sm)
+{
+       return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING;
+}
+
+/* Should we fix it? */
+static inline bool needs_repair(struct xfs_scrub_metadata *sm)
+{
+       return is_corrupt(sm) || xref_disagrees(sm);
+}
+
+/* Warn about strange circumstances after scrub. */
+static inline void
+xfs_scrub_warn_incomplete_scrub(
+       struct scrub_ctx                *ctx,
+       const char                      *descr,
+       struct xfs_scrub_metadata       *meta)
+{
+       if (is_incomplete(meta))
+               str_info(ctx, descr, _("Check incomplete."));
+
+       if (is_suspicious(meta)) {
+               if (debug)
+                       str_info(ctx, descr, _("Possibly suspect metadata."));
+               else
+                       str_warn(ctx, descr, _("Possibly suspect metadata."));
+       }
+
+       if (xref_failed(meta))
+               str_info(ctx, descr, _("Cross-referencing failed."));
+}
+
+/* Do a read-only check of some metadata. */
+static enum check_outcome
+xfs_check_metadata(
+       struct scrub_ctx                *ctx,
+       int                             fd,
+       struct xfs_scrub_metadata       *meta,
+       bool                            is_inode)
+{
+       char                            buf[DESCR_BUFSZ];
+       unsigned int                    tries = 0;
+       int                             code;
+       int                             error;
+
+       assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
+       assert(meta->sm_type < XFS_SCRUB_TYPE_NR);
+       format_scrub_descr(buf, DESCR_BUFSZ, meta, &scrubbers[meta->sm_type]);
+
+       dbg_printf("check %s flags %xh\n", buf, meta->sm_flags);
+retry:
+       error = ioctl(fd, XFS_IOC_SCRUB_METADATA, meta);
+       if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
+               meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+       if (error) {
+               code = errno;
+               switch (code) {
+               case ENOENT:
+                       /* Metadata not present, just skip it. */
+                       return CHECK_DONE;
+               case ESHUTDOWN:
+                       /* FS already crashed, give up. */
+                       str_error(ctx, buf,
+_("Filesystem is shut down, aborting."));
+                       return CHECK_ABORT;
+               case EIO:
+               case ENOMEM:
+                       /* Abort on I/O errors or insufficient memory. */
+                       str_errno(ctx, buf);
+                       return CHECK_ABORT;
+               case EDEADLOCK:
+               case EBUSY:
+               case EFSBADCRC:
+               case EFSCORRUPTED:
+                       /*
+                        * The first two should never escape the kernel,
+                        * and the other two should be reported via sm_flags.
+                        */
+                       str_error(ctx, buf,
+_("Kernel bug!  errno=%d"), code);
+                       /* fall through */
+               default:
+                       /* Operational error. */
+                       str_errno(ctx, buf);
+                       return CHECK_DONE;
+               }
+       }
+
+       /*
+        * If the kernel says the test was incomplete or that there was
+        * a cross-referencing discrepancy but no obvious corruption,
+        * we'll try the scan again, just in case the fs was busy.
+        * Only retry so many times.
+        */
+       if (tries < 10 && (is_incomplete(meta) ||
+                          (xref_disagrees(meta) && !is_corrupt(meta)))) {
+               tries++;
+               goto retry;
+       }
+
+       /* Complain about incomplete or suspicious metadata. */
+       xfs_scrub_warn_incomplete_scrub(ctx, buf, meta);
+
+       /*
+        * If we need repairs or there were discrepancies, schedule a
+        * repair if desired, otherwise complain.
+        */
+       if (is_corrupt(meta) || xref_disagrees(meta)) {
+               if (ctx->mode < SCRUB_MODE_REPAIR) {
+                       str_error(ctx, buf,
+_("Repairs are required."));
+                       return CHECK_DONE;
+               }
+
+               return CHECK_REPAIR;
+       }
+
+       /*
+        * If we could optimize, schedule a repair if desired,
+        * otherwise complain.
+        */
+       if (is_unoptimized(meta)) {
+               if (ctx->mode < SCRUB_MODE_PREEN) {
+                       if (!is_inode) {
+                               /* AG or FS metadata, always warn. */
+                               str_info(ctx, buf,
+_("Optimization is possible."));
+                       } else if (!ctx->preen_triggers[meta->sm_type]) {
+                               /* File metadata, only warn once per type. */
+                               pthread_mutex_lock(&ctx->lock);
+                               if (!ctx->preen_triggers[meta->sm_type])
+                                       ctx->preen_triggers[meta->sm_type] = true;
+                               pthread_mutex_unlock(&ctx->lock);
+                       }
+                       return CHECK_DONE;
+               }
+
+               return CHECK_REPAIR;
+       }
+
+       /* Everything is ok. */
+       return CHECK_DONE;
+}
+
+/* Bulk-notify user about things that could be optimized. */
+void
+xfs_scrub_report_preen_triggers(
+       struct scrub_ctx                *ctx)
+{
+       int                             i;
+
+       for (i = 0; i < XFS_SCRUB_TYPE_NR; i++) {
+               pthread_mutex_lock(&ctx->lock);
+               if (ctx->preen_triggers[i]) {
+                       ctx->preen_triggers[i] = false;
+                       pthread_mutex_unlock(&ctx->lock);
+                       str_info(ctx, ctx->mntpoint,
+_("Optimizations of %s are possible."), scrubbers[i].name);
+               } else {
+                       pthread_mutex_unlock(&ctx->lock);
+               }
+       }
+}
+
+/* Scrub metadata, saving corruption reports for later. */
+static bool
+xfs_scrub_metadata(
+       struct scrub_ctx                *ctx,
+       enum scrub_type                 scrub_type,
+       xfs_agnumber_t                  agno)
+{
+       struct xfs_scrub_metadata       meta = {0};
+       const struct scrub_descr        *sc;
+       enum check_outcome              fix;
+       int                             type;
+
+       sc = scrubbers;
+       for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
+               if (sc->type != scrub_type)
+                       continue;
+
+               meta.sm_type = type;
+               meta.sm_flags = 0;
+               meta.sm_agno = agno;
+               background_sleep();
+
+               /* Check the item. */
+               fix = xfs_check_metadata(ctx, ctx->mnt_fd, &meta, false);
+               switch (fix) {
+               case CHECK_ABORT:
+                       return false;
+               case CHECK_REPAIR:
+                       /* fall through */
+               case CHECK_DONE:
+                       continue;
+               case CHECK_RETRY:
+                       abort();
+                       break;
+               }
+       }
+
+       return true;
+}
+
+/*
+ * Scrub primary superblock.  This will be useful if we ever need to hook
+ * a filesystem-wide pre-scrub activity off of the sb 0 scrubber (which
+ * currently does nothing).
+ */
+bool
+xfs_scrub_primary_super(
+       struct scrub_ctx                *ctx)
+{
+       struct xfs_scrub_metadata       meta = {
+               .sm_type = XFS_SCRUB_TYPE_SB,
+       };
+       enum check_outcome              fix;
+
+       /* Check the item. */
+       fix = xfs_check_metadata(ctx, ctx->mnt_fd, &meta, false);
+       switch (fix) {
+       case CHECK_ABORT:
+               return false;
+       case CHECK_REPAIR:
+               /* fall through */
+       case CHECK_DONE:
+               return true;
+       case CHECK_RETRY:
+               abort();
+               break;
+       }
+
+       return true;
+}
+
+/* Scrub each AG's header blocks. */
+bool
+xfs_scrub_ag_headers(
+       struct scrub_ctx                *ctx,
+       xfs_agnumber_t                  agno)
+{
+       return xfs_scrub_metadata(ctx, ST_AGHEADER, agno);
+}
+
+/* Scrub each AG's metadata btrees. */
+bool
+xfs_scrub_ag_metadata(
+       struct scrub_ctx                *ctx,
+       xfs_agnumber_t                  agno)
+{
+       return xfs_scrub_metadata(ctx, ST_PERAG, agno);
+}
+
+/* Scrub whole-FS metadata btrees. */
+bool
+xfs_scrub_fs_metadata(
+       struct scrub_ctx                *ctx)
+{
+       return xfs_scrub_metadata(ctx, ST_FS, 0);
+}
+
+/* Scrub inode metadata. */
+static bool
+__xfs_scrub_file(
+       struct scrub_ctx                *ctx,
+       uint64_t                        ino,
+       uint32_t                        gen,
+       int                             fd,
+       unsigned int                    type)
+{
+       struct xfs_scrub_metadata       meta = {0};
+       enum check_outcome              fix;
+
+       assert(type < XFS_SCRUB_TYPE_NR);
+       assert(scrubbers[type].type == ST_INODE);
+
+       meta.sm_type = type;
+       meta.sm_ino = ino;
+       meta.sm_gen = gen;
+
+       /* Scrub the piece of metadata. */
+       fix = xfs_check_metadata(ctx, fd, &meta, true);
+       if (fix == CHECK_ABORT)
+               return false;
+       if (fix == CHECK_DONE)
+               return true;
+
+       return true;
+}
+
+bool
+xfs_scrub_inode_fields(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_INODE);
+}
+
+bool
+xfs_scrub_data_fork(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTD);
+}
+
+bool
+xfs_scrub_attr_fork(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTA);
+}
+
+bool
+xfs_scrub_cow_fork(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTC);
+}
+
+bool
+xfs_scrub_dir(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_DIR);
+}
+
+bool
+xfs_scrub_attr(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_XATTR);
+}
+
+bool
+xfs_scrub_symlink(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_SYMLINK);
+}
+
+bool
+xfs_scrub_parent(
+       struct scrub_ctx        *ctx,
+       uint64_t                ino,
+       uint32_t                gen,
+       int                     fd)
+{
+       return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_PARENT);
+}
+
+/* Test the availability of a kernel scrub command. */
+static bool
+__xfs_scrub_test(
+       struct scrub_ctx                *ctx,
+       unsigned int                    type,
+       bool                            repair)
+{
+       struct xfs_scrub_metadata       meta = {0};
+       int                             error;
+
+       if (debug_tweak_on("XFS_SCRUB_NO_KERNEL"))
+               return false;
+
+       meta.sm_type = type;
+       if (repair)
+               meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
+       error = ioctl(ctx->mnt_fd, XFS_IOC_SCRUB_METADATA, &meta);
+       if (!error)
+               return true;
+       switch (errno) {
+       case EROFS:
+               str_info(ctx, ctx->mntpoint,
+_("Filesystem is mounted read-only; cannot proceed."));
+               return false;
+       case ENOTRECOVERABLE:
+               str_info(ctx, ctx->mntpoint,
+_("Filesystem is mounted norecovery; cannot proceed."));
+               return false;
+       case EOPNOTSUPP:
+       case ENOTTY:
+               if (debug || verbose)
+                       str_info(ctx, ctx->mntpoint,
+_("Kernel %s %s facility not detected."),
+                                       _(scrubbers[type].name),
+                                       repair ? _("repair") : _("scrub"));
+               return false;
+       case ENOENT:
+               /* Scrubber says not present on this fs; that's fine. */
+               return true;
+       default:
+               str_info(ctx, ctx->mntpoint, "%s", strerror(errno));
+               return true;
+       }
+       return error == 0 || (error && errno != EOPNOTSUPP && errno != ENOTTY);
+}
+
+bool
+xfs_can_scrub_fs_metadata(
+       struct scrub_ctx        *ctx)
+{
+       return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, false);
+}
+
+bool
+xfs_can_scrub_inode(
+       struct scrub_ctx        *ctx)
+{
+       return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_INODE, false);
+}
+
+bool
+xfs_can_scrub_bmap(
+       struct scrub_ctx        *ctx)
+{
+       return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_BMBTD, false);
+}
+
+bool
+xfs_can_scrub_dir(
+       struct scrub_ctx        *ctx)
+{
+       return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_DIR, false);
+}
+
+bool
+xfs_can_scrub_attr(
+       struct scrub_ctx        *ctx)
+{
+       return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_XATTR, false);
+}
+
+bool
+xfs_can_scrub_symlink(
+       struct scrub_ctx        *ctx)
+{
+       return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_SYMLINK, false);
+}
+
+bool
+xfs_can_scrub_parent(
+       struct scrub_ctx        *ctx)
+{
+       return __xfs_scrub_test(ctx, XFS_SCRUB_TYPE_PARENT, false);
+}
diff --git a/scrub/scrub.h b/scrub/scrub.h
new file mode 100644 (file)
index 0000000..0b454df
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef XFS_SCRUB_SCRUB_H_
+#define XFS_SCRUB_SCRUB_H_
+
+/* Online scrub and repair. */
+enum check_outcome {
+       CHECK_DONE,     /* no further processing needed */
+       CHECK_REPAIR,   /* schedule this for repairs */
+       CHECK_ABORT,    /* end program */
+       CHECK_RETRY,    /* repair failed, try again later */
+};
+
+void xfs_scrub_report_preen_triggers(struct scrub_ctx *ctx);
+bool xfs_scrub_primary_super(struct scrub_ctx *ctx);
+bool xfs_scrub_ag_headers(struct scrub_ctx *ctx, xfs_agnumber_t agno);
+bool xfs_scrub_ag_metadata(struct scrub_ctx *ctx, xfs_agnumber_t agno);
+bool xfs_scrub_fs_metadata(struct scrub_ctx *ctx);
+
+bool xfs_can_scrub_fs_metadata(struct scrub_ctx *ctx);
+bool xfs_can_scrub_inode(struct scrub_ctx *ctx);
+bool xfs_can_scrub_bmap(struct scrub_ctx *ctx);
+bool xfs_can_scrub_dir(struct scrub_ctx *ctx);
+bool xfs_can_scrub_attr(struct scrub_ctx *ctx);
+bool xfs_can_scrub_symlink(struct scrub_ctx *ctx);
+bool xfs_can_scrub_parent(struct scrub_ctx *ctx);
+
+bool xfs_scrub_inode_fields(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+bool xfs_scrub_data_fork(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+bool xfs_scrub_attr_fork(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+bool xfs_scrub_cow_fork(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+bool xfs_scrub_dir(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+bool xfs_scrub_attr(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+bool xfs_scrub_symlink(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+bool xfs_scrub_parent(struct scrub_ctx *ctx, uint64_t ino, uint32_t gen,
+               int fd);
+
+#endif /* XFS_SCRUB_SCRUB_H_ */