+// SPDX-License-Identifier: GPL-2.0+
/*
* 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 "xfs.h"
#include <stdint.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include "list.h"
-#include "path.h"
+#include "libfrog/paths.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/scrub.h"
#include "xfs_scrub.h"
#include "common.h"
#include "progress.h"
#include "scrub.h"
#include "xfs_errortag.h"
+#include "repair.h"
+#include "descr.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
+static int
format_scrub_descr(
+ struct scrub_ctx *ctx,
char *buf,
size_t buflen,
- struct xfs_scrub_metadata *meta,
- const struct scrub_descr *sc)
+ void *where)
{
+ struct xfs_scrub_metadata *meta = where;
+ const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[meta->sm_type];
+
switch (sc->type) {
- case ST_AGHEADER:
- case ST_PERAG:
- snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
- _(sc->name));
+ case XFROG_SCRUB_TYPE_AGHEADER:
+ case XFROG_SCRUB_TYPE_PERAG:
+ return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
+ _(sc->descr));
break;
- case ST_INODE:
- snprintf(buf, buflen, _("Inode %"PRIu64" %s"),
- (uint64_t)meta->sm_ino, _(sc->name));
+ case XFROG_SCRUB_TYPE_INODE:
+ return scrub_render_ino_descr(ctx, buf, buflen,
+ meta->sm_ino, meta->sm_gen, "%s",
+ _(sc->descr));
break;
- case ST_FS:
- snprintf(buf, buflen, _("%s"), _(sc->name));
+ case XFROG_SCRUB_TYPE_FS:
+ return snprintf(buf, buflen, _("%s"), _(sc->descr));
break;
- case ST_NONE:
+ case XFROG_SCRUB_TYPE_NONE:
assert(0);
break;
}
+ return -1;
}
/* Predicates for scrub flag state. */
static inline void
xfs_scrub_warn_incomplete_scrub(
struct scrub_ctx *ctx,
- const char *descr,
+ struct descr *dsc,
struct xfs_scrub_metadata *meta)
{
if (is_incomplete(meta))
- str_info(ctx, descr, _("Check incomplete."));
+ str_info(ctx, descr_render(dsc), _("Check incomplete."));
if (is_suspicious(meta)) {
if (debug)
- str_info(ctx, descr, _("Possibly suspect metadata."));
+ str_info(ctx, descr_render(dsc),
+ _("Possibly suspect metadata."));
else
- str_warn(ctx, descr, _("Possibly suspect metadata."));
+ str_warn(ctx, descr_render(dsc),
+ _("Possibly suspect metadata."));
}
if (xref_failed(meta))
- str_info(ctx, descr, _("Cross-referencing failed."));
+ str_info(ctx, descr_render(dsc),
+ _("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];
+ DEFINE_DESCR(dsc, ctx, format_scrub_descr);
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]);
+ descr_set(&dsc, meta);
- dbg_printf("check %s flags %xh\n", buf, meta->sm_flags);
+ dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta->sm_flags);
retry:
- error = ioctl(fd, XFS_IOC_SCRUB_METADATA, meta);
+ error = xfrog_scrub_metadata(&ctx->mnt, meta);
if (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR") && !error)
meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
if (error) {
return CHECK_DONE;
case ESHUTDOWN:
/* FS already crashed, give up. */
- str_info(ctx, buf,
+ str_error(ctx, descr_render(&dsc),
_("Filesystem is shut down, aborting."));
return CHECK_ABORT;
case EIO:
case ENOMEM:
/* Abort on I/O errors or insufficient memory. */
- str_errno(ctx, buf);
+ str_errno(ctx, descr_render(&dsc));
return CHECK_ABORT;
case EDEADLOCK:
case EBUSY:
* The first two should never escape the kernel,
* and the other two should be reported via sm_flags.
*/
- str_info(ctx, buf,
-_("Kernel bug! errno=%d"), code);
+ str_liberror(ctx, code, _("Kernel bug"));
/* fall through */
default:
/* Operational error. */
- str_errno(ctx, buf);
+ str_errno(ctx, descr_render(&dsc));
return CHECK_DONE;
}
}
}
/* Complain about incomplete or suspicious metadata. */
- xfs_scrub_warn_incomplete_scrub(ctx, buf, meta);
+ xfs_scrub_warn_incomplete_scrub(ctx, &dsc, meta);
/*
* If we need repairs or there were discrepancies, schedule a
*/
if (is_corrupt(meta) || xref_disagrees(meta)) {
if (ctx->mode < SCRUB_MODE_REPAIR) {
- str_error(ctx, buf,
+ str_corrupt(ctx, descr_render(&dsc),
_("Repairs are required."));
return CHECK_DONE;
}
if (ctx->mode != SCRUB_MODE_REPAIR) {
if (!is_inode) {
/* AG or FS metadata, always warn. */
- str_info(ctx, buf,
+ str_info(ctx, descr_render(&dsc),
_("Optimization is possible."));
} else if (!ctx->preen_triggers[meta->sm_type]) {
/* File metadata, only warn once per type. */
ctx->preen_triggers[i] = false;
pthread_mutex_unlock(&ctx->lock);
str_info(ctx, ctx->mntpoint,
-_("Optimizations of %s are possible."), scrubbers[i].name);
+_("Optimizations of %s are possible."), _(xfrog_scrubbers[i].descr));
} else {
pthread_mutex_unlock(&ctx->lock);
}
}
}
-/* Scrub metadata, saving corruption reports for later. */
-static bool
-xfs_scrub_metadata(
+/* Save a scrub context for later repairs. */
+static int
+xfs_scrub_save_repair(
struct scrub_ctx *ctx,
- enum scrub_type scrub_type,
- xfs_agnumber_t agno)
+ struct action_list *alist,
+ struct xfs_scrub_metadata *meta)
{
- struct xfs_scrub_metadata meta = {0};
- const struct scrub_descr *sc;
- enum check_outcome fix;
- int type;
+ struct action_item *aitem;
- sc = scrubbers;
- for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
- if (sc->type != scrub_type)
- continue;
+ /* Schedule this item for later repairs. */
+ aitem = malloc(sizeof(struct action_item));
+ if (!aitem) {
+ str_errno(ctx, _("adding item to repair list"));
+ return errno;
+ }
- 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);
- progress_add(1);
- switch (fix) {
- case CHECK_ABORT:
- return false;
- case CHECK_REPAIR:
- /* fall through */
- case CHECK_DONE:
- continue;
- case CHECK_RETRY:
- abort();
- break;
- }
+ 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:
+ aitem->agno = meta->sm_agno;
+ break;
+ case XFROG_SCRUB_TYPE_INODE:
+ aitem->ino = meta->sm_ino;
+ aitem->gen = meta->sm_gen;
+ break;
+ default:
+ break;
}
- return true;
+ action_list_add(alist, aitem);
+ return 0;
}
/*
- * 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).
+ * Scrub a single XFS_SCRUB_TYPE_*, saving corruption reports for later.
+ *
+ * Returns 0 for success. If errors occur, this function will log them and
+ * return a positive error code.
*/
-bool
-xfs_scrub_primary_super(
- struct scrub_ctx *ctx)
+static int
+xfs_scrub_meta_type(
+ struct scrub_ctx *ctx,
+ unsigned int type,
+ xfs_agnumber_t agno,
+ struct action_list *alist)
{
struct xfs_scrub_metadata meta = {
- .sm_type = XFS_SCRUB_TYPE_SB,
+ .sm_type = type,
+ .sm_agno = agno,
};
enum check_outcome fix;
+ int ret;
+
+ background_sleep();
/* Check the item. */
- fix = xfs_check_metadata(ctx, ctx->mnt_fd, &meta, false);
+ fix = xfs_check_metadata(ctx, &meta, false);
+ progress_add(1);
+
switch (fix) {
case CHECK_ABORT:
- return false;
+ return ECANCELED;
case CHECK_REPAIR:
+ ret = xfs_scrub_save_repair(ctx, alist, &meta);
+ if (ret)
+ return ret;
/* fall through */
case CHECK_DONE:
- return true;
- case CHECK_RETRY:
+ return 0;
+ default:
+ /* CHECK_RETRY should never happen. */
abort();
- break;
+ }
+}
+
+/*
+ * Scrub all metadata types that are assigned to the given XFROG_SCRUB_TYPE_*,
+ * saving corruption reports for later. This should not be used for
+ * XFROG_SCRUB_TYPE_INODE or for checking summary metadata.
+ */
+static bool
+xfs_scrub_all_types(
+ struct scrub_ctx *ctx,
+ enum xfrog_scrub_type scrub_type,
+ xfs_agnumber_t agno,
+ struct action_list *alist)
+{
+ const struct xfrog_scrub_descr *sc;
+ unsigned int type;
+
+ sc = xfrog_scrubbers;
+ 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)
+ continue;
+
+ ret = xfs_scrub_meta_type(ctx, type, agno, alist);
+ if (ret)
+ return ret;
}
- return true;
+ return 0;
+}
+
+/*
+ * 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). If errors occur, this function will log them and
+ * return nonzero.
+ */
+int
+xfs_scrub_primary_super(
+ struct scrub_ctx *ctx,
+ struct action_list *alist)
+{
+ return xfs_scrub_meta_type(ctx, XFS_SCRUB_TYPE_SB, 0, alist);
}
/* Scrub each AG's header blocks. */
-bool
+int
xfs_scrub_ag_headers(
struct scrub_ctx *ctx,
- xfs_agnumber_t agno)
+ xfs_agnumber_t agno,
+ struct action_list *alist)
{
- return xfs_scrub_metadata(ctx, ST_AGHEADER, agno);
+ return xfs_scrub_all_types(ctx, XFROG_SCRUB_TYPE_AGHEADER, agno, alist);
}
/* Scrub each AG's metadata btrees. */
-bool
+int
xfs_scrub_ag_metadata(
struct scrub_ctx *ctx,
- xfs_agnumber_t agno)
+ xfs_agnumber_t agno,
+ struct action_list *alist)
{
- return xfs_scrub_metadata(ctx, ST_PERAG, agno);
+ return xfs_scrub_all_types(ctx, XFROG_SCRUB_TYPE_PERAG, agno, alist);
}
/* Scrub whole-FS metadata btrees. */
-bool
+int
xfs_scrub_fs_metadata(
- struct scrub_ctx *ctx)
+ struct scrub_ctx *ctx,
+ struct action_list *alist)
{
- return xfs_scrub_metadata(ctx, ST_FS, 0);
+ return xfs_scrub_all_types(ctx, XFROG_SCRUB_TYPE_FS, 0, alist);
+}
+
+/* Scrub FS summary metadata. */
+int
+xfs_scrub_fs_summary(
+ struct scrub_ctx *ctx,
+ struct action_list *alist)
+{
+ return xfs_scrub_meta_type(ctx, XFS_SCRUB_TYPE_FSCOUNTERS, 0, alist);
}
/* How many items do we have to check? */
unsigned int
-xfs_scrub_estimate_ag_work(
+scrub_estimate_ag_work(
struct scrub_ctx *ctx)
{
- const struct scrub_descr *sc;
+ const struct xfrog_scrub_descr *sc;
int type;
unsigned int estimate = 0;
- sc = scrubbers;
+ sc = xfrog_scrubbers;
for (type = 0; type < XFS_SCRUB_TYPE_NR; type++, sc++) {
switch (sc->type) {
- case ST_AGHEADER:
- case ST_PERAG:
- estimate += ctx->geo.agcount;
+ case XFROG_SCRUB_TYPE_AGHEADER:
+ case XFROG_SCRUB_TYPE_PERAG:
+ estimate += ctx->mnt.fsgeom.agcount;
break;
- case ST_FS:
+ case XFROG_SCRUB_TYPE_FS:
estimate++;
break;
default:
return estimate;
}
-/* Scrub inode metadata. */
-static bool
+/*
+ * Scrub inode metadata. If errors occur, this function will log them and
+ * return nonzero.
+ */
+static int
__xfs_scrub_file(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd,
- unsigned int type)
+ unsigned int type,
+ struct action_list *alist)
{
struct xfs_scrub_metadata meta = {0};
enum check_outcome fix;
assert(type < XFS_SCRUB_TYPE_NR);
- assert(scrubbers[type].type == ST_INODE);
+ assert(xfrog_scrubbers[type].type == XFROG_SCRUB_TYPE_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);
+ fix = xfs_check_metadata(ctx, &meta, true);
if (fix == CHECK_ABORT)
- return false;
+ return ECANCELED;
if (fix == CHECK_DONE)
- return true;
+ return 0;
- return true;
+ return xfs_scrub_save_repair(ctx, alist, &meta);
}
-bool
+int
xfs_scrub_inode_fields(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_INODE);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_INODE, alist);
}
-bool
+int
xfs_scrub_data_fork(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTD);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTD, alist);
}
-bool
+int
xfs_scrub_attr_fork(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTA);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTA, alist);
}
-bool
+int
xfs_scrub_cow_fork(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_BMBTC);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_BMBTC, alist);
}
-bool
+int
xfs_scrub_dir(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_DIR);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_DIR, alist);
}
-bool
+int
xfs_scrub_attr(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_XATTR);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_XATTR, alist);
}
-bool
+int
xfs_scrub_symlink(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_SYMLINK);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_SYMLINK, alist);
}
-bool
+int
xfs_scrub_parent(
struct scrub_ctx *ctx,
uint64_t ino,
uint32_t gen,
- int fd)
+ struct action_list *alist)
{
- return __xfs_scrub_file(ctx, ino, gen, fd, XFS_SCRUB_TYPE_PARENT);
+ return __xfs_scrub_file(ctx, ino, gen, XFS_SCRUB_TYPE_PARENT, alist);
}
-/* Test the availability of a kernel scrub command. */
+/*
+ * 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
+ * return false.
+ */
static bool
__xfs_scrub_test(
struct scrub_ctx *ctx,
bool repair)
{
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) {
- str_info(ctx, "XFS_SCRUB_FORCE_REPAIR", "Not supported.");
- return false;
+ 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;
- error = ioctl(ctx->mnt_fd, XFS_IOC_SCRUB_METADATA, &meta);
+ error = xfrog_scrub_metadata(&ctx->mnt, &meta);
if (!error)
return true;
switch (errno) {
if (debug || verbose)
str_info(ctx, ctx->mntpoint,
_("Kernel %s %s facility not detected."),
- _(scrubbers[type].name),
+ _(xfrog_scrubbers[type].descr),
repair ? _("repair") : _("scrub"));
return false;
case ENOENT:
xfs_repair_metadata(
struct scrub_ctx *ctx,
int fd,
- struct repair_item *ri,
+ struct action_item *aitem,
unsigned int repair_flags)
{
- char buf[DESCR_BUFSZ];
struct xfs_scrub_metadata meta = { 0 };
struct xfs_scrub_metadata oldm;
+ DEFINE_DESCR(dsc, ctx, format_scrub_descr);
int error;
- assert(ri->type < XFS_SCRUB_TYPE_NR);
+ assert(aitem->type < XFS_SCRUB_TYPE_NR);
assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
- meta.sm_type = ri->type;
- meta.sm_flags = ri->flags | XFS_SCRUB_IFLAG_REPAIR;
- switch (scrubbers[ri->type].type) {
- case ST_AGHEADER:
- case ST_PERAG:
- meta.sm_agno = ri->agno;
+ 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:
+ meta.sm_agno = aitem->agno;
break;
- case ST_INODE:
- meta.sm_ino = ri->ino;
- meta.sm_gen = ri->gen;
+ case XFROG_SCRUB_TYPE_INODE:
+ meta.sm_ino = aitem->ino;
+ meta.sm_gen = aitem->gen;
break;
default:
break;
}
- /*
- * If this is a preen operation but we're only repairing
- * critical items, defer the preening until later.
- */
- if (!needs_repair(&meta) && (repair_flags & XRM_REPAIR_ONLY))
+ if (!is_corrupt(&meta) && (repair_flags & XRM_REPAIR_ONLY))
return CHECK_RETRY;
memcpy(&oldm, &meta, sizeof(oldm));
- format_scrub_descr(buf, DESCR_BUFSZ, &meta, &scrubbers[meta.sm_type]);
+ descr_set(&dsc, &oldm);
if (needs_repair(&meta))
- str_info(ctx, buf, _("Attempting repair."));
+ str_info(ctx, descr_render(&dsc), _("Attempting repair."));
else if (debug || verbose)
- str_info(ctx, buf, _("Attempting optimization."));
+ str_info(ctx, descr_render(&dsc),
+ _("Attempting optimization."));
- error = ioctl(fd, XFS_IOC_SCRUB_METADATA, &meta);
- /*
- * If the caller doesn't want us to complain, tell the caller to
- * requeue the repair for later and don't say a thing.
- */
- if (!(repair_flags & XRM_NOFIX_COMPLAIN) &&
- (error || needs_repair(&meta)))
- return CHECK_RETRY;
+ error = xfrog_scrub_metadata(&ctx->mnt, &meta);
if (error) {
switch (errno) {
case EDEADLOCK:
case EBUSY:
/* Filesystem is busy, try again later. */
if (debug || verbose)
- str_info(ctx, buf,
+ str_info(ctx, descr_render(&dsc),
_("Filesystem is busy, deferring repair."));
return CHECK_RETRY;
case ESHUTDOWN:
/* Filesystem is already shut down, abort. */
- str_info(ctx, buf,
+ str_error(ctx, descr_render(&dsc),
_("Filesystem is shut down, aborting."));
return CHECK_ABORT;
case ENOTTY:
case EOPNOTSUPP:
/*
- * If we forced repairs, don't complain if kernel
- * doesn't know how to fix.
+ * 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_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 (debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
+ if (is_unoptimized(&oldm) ||
+ debug_tweak_on("XFS_SCRUB_FORCE_REPAIR"))
return CHECK_DONE;
/* fall through */
case EINVAL:
/* Kernel doesn't know how to repair this? */
- str_error(ctx, buf,
+ str_corrupt(ctx, descr_render(&dsc),
_("Don't know how to fix; offline repair required."));
return CHECK_DONE;
case EROFS:
/* Read-only filesystem, can't fix. */
if (verbose || debug || needs_repair(&oldm))
- str_info(ctx, buf,
+ str_error(ctx, descr_render(&dsc),
_("Read-only filesystem; cannot make changes."));
- return CHECK_DONE;
+ return CHECK_ABORT;
case ENOENT:
/* Metadata not present, just skip it. */
return CHECK_DONE;
return CHECK_DONE;
/* fall through */
default:
- /* Operational error. */
- str_errno(ctx, buf);
+ /*
+ * 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 error and bail out.
+ */
+ if (!(repair_flags & XRM_COMPLAIN_IF_UNFIXED))
+ return CHECK_RETRY;
+ str_errno(ctx, descr_render(&dsc));
return CHECK_DONE;
}
}
- if (repair_flags & XRM_NOFIX_COMPLAIN)
- xfs_scrub_warn_incomplete_scrub(ctx, buf, &meta);
+ if (repair_flags & XRM_COMPLAIN_IF_UNFIXED)
+ xfs_scrub_warn_incomplete_scrub(ctx, &dsc, &meta);
if (needs_repair(&meta)) {
- /* Still broken, try again or fix offline. */
- if (repair_flags & XRM_NOFIX_COMPLAIN)
- str_error(ctx, buf,
+ /*
+ * 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_COMPLAIN_IF_UNFIXED))
+ return CHECK_RETRY;
+ str_corrupt(ctx, descr_render(&dsc),
_("Repair unsuccessful; offline repair required."));
} else {
/* Clean operation, no corruption detected. */
if (needs_repair(&oldm))
- record_repair(ctx, buf, _("Repairs successful."));
+ record_repair(ctx, descr_render(&dsc),
+ _("Repairs successful."));
else
- record_preen(ctx, buf, _("Optimization successful."));
+ record_preen(ctx, descr_render(&dsc),
+ _("Optimization successful."));
}
return CHECK_DONE;
}