blob: 403b3a7282eacdaa58217f8134a096609f9d268b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*/
#include <sys/uio.h>
#include <xfs/xfs.h>
#include "command.h"
#include "input.h"
#include "init.h"
#include "libfrog/paths.h"
#include "libfrog/fsgeom.h"
#include "libfrog/scrub.h"
#include "io.h"
static struct cmdinfo scrub_cmd;
static struct cmdinfo repair_cmd;
static void
scrub_help(void)
{
const struct xfrog_scrub_descr *d;
int i;
printf(_(
"\n"
" Scrubs a piece of XFS filesystem metadata. The first argument is the type\n"
" of metadata to examine. Allocation group metadata types take one AG number\n"
" as the second parameter. Inode metadata types act on the currently open file\n"
" or (optionally) take an inode number and generation number to act upon as\n"
" the second and third parameters.\n"
"\n"
" Example:\n"
" 'scrub inobt 3' - scrub the inode btree in AG 3.\n"
" 'scrub bmapbtd 128 13525' - scrubs the extent map of inode 128 gen 13525.\n"
"\n"
" Known metadata scrub types are:"));
for (i = 0, d = xfrog_scrubbers; i < XFS_SCRUB_TYPE_NR; i++, d++)
printf(" %s", d->name);
printf("\n");
}
static bool
parse_inode(
int argc,
char **argv,
int optind,
__u64 *ino,
__u32 *gen)
{
char *p;
unsigned long long control;
unsigned long control2;
if (optind == argc) {
*ino = 0;
*gen = 0;
return true;
}
if (optind != argc - 2) {
fprintf(stderr,
_("Must specify inode number and generation.\n"));
return false;
}
control = strtoull(argv[optind], &p, 0);
if (*p != '\0') {
fprintf(stderr, _("Bad inode number '%s'.\n"),
argv[optind]);
return false;
}
control2 = strtoul(argv[optind + 1], &p, 0);
if (*p != '\0') {
fprintf(stderr, _("Bad generation number '%s'.\n"),
argv[optind + 1]);
return false;
}
*ino = control;
*gen = control2;
return true;
}
static bool
parse_agno(
int argc,
char **argv,
int optind,
__u32 *agno)
{
char *p;
unsigned long control;
if (optind != argc - 1) {
fprintf(stderr, _("Must specify one AG number.\n"));
return false;
}
control = strtoul(argv[optind], &p, 0);
if (*p != '\0') {
fprintf(stderr, _("Bad AG number '%s'.\n"), argv[optind]);
return false;
}
*agno = control;
return true;
}
static bool
parse_none(
int argc,
int optind)
{
if (optind != argc) {
fprintf(stderr, _("No parameters allowed.\n"));
return false;
}
/* no control parameters */
return true;
}
static int
parse_args(
int argc,
char **argv,
const struct cmdinfo *cmdinfo,
struct xfs_scrub_metadata *meta)
{
int type = -1;
int i, c;
uint32_t flags = 0;
const struct xfrog_scrub_descr *d = NULL;
memset(meta, 0, sizeof(struct xfs_scrub_metadata));
while ((c = getopt(argc, argv, "R")) != EOF) {
switch (c) {
case 'R':
flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
break;
default:
exitcode = 1;
return command_usage(cmdinfo);
}
}
if (optind > argc - 1) {
exitcode = 1;
return command_usage(cmdinfo);
}
for (i = 0, d = xfrog_scrubbers; i < XFS_SCRUB_TYPE_NR; i++, d++) {
if (strcmp(d->name, argv[optind]) == 0) {
type = i;
break;
}
}
if (type < 0) {
printf(_("Unknown type '%s'.\n"), argv[optind]);
exitcode = 1;
return command_usage(cmdinfo);
}
optind++;
meta->sm_type = type;
meta->sm_flags = flags;
switch (d->type) {
case XFROG_SCRUB_TYPE_INODE:
if (!parse_inode(argc, argv, optind, &meta->sm_ino,
&meta->sm_gen)) {
exitcode = 1;
return command_usage(cmdinfo);
}
break;
case XFROG_SCRUB_TYPE_AGHEADER:
case XFROG_SCRUB_TYPE_PERAG:
if (!parse_agno(argc, argv, optind, &meta->sm_agno)) {
exitcode = 1;
return command_usage(cmdinfo);
}
break;
case XFROG_SCRUB_TYPE_FS:
case XFROG_SCRUB_TYPE_NONE:
if (!parse_none(argc, optind)) {
exitcode = 1;
return command_usage(cmdinfo);
}
break;
default:
ASSERT(0);
break;
}
return 0;
}
static int
scrub_f(
int argc,
char **argv)
{
struct xfs_scrub_metadata meta;
int error;
error = parse_args(argc, argv, &scrub_cmd, &meta);
if (error)
return error;
error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta);
if (error)
perror("scrub");
if (meta.sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
printf(_("Corruption detected.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_PREEN)
printf(_("Optimization possible.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_XFAIL)
printf(_("Cross-referencing failed.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
printf(_("Corruption detected during cross-referencing.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
printf(_("Scan was not complete.\n"));
return 0;
}
void
scrub_init(void)
{
scrub_cmd.name = "scrub";
scrub_cmd.altname = "sc";
scrub_cmd.cfunc = scrub_f;
scrub_cmd.argmin = 1;
scrub_cmd.argmax = -1;
scrub_cmd.flags = CMD_NOMAP_OK;
scrub_cmd.args = _("type [agno|ino gen]");
scrub_cmd.oneline = _("scrubs filesystem metadata");
scrub_cmd.help = scrub_help;
add_command(&scrub_cmd);
}
static void
repair_help(void)
{
const struct xfrog_scrub_descr *d;
int i;
printf(_(
"\n"
" Repairs a piece of XFS filesystem metadata. The first argument is the type\n"
" of metadata to examine. Allocation group metadata types take one AG number\n"
" as the second parameter. Inode metadata types act on the currently open file\n"
" or (optionally) take an inode number and generation number to act upon as\n"
" the second and third parameters.\n"
"\n"
" Flags are -R to force rebuilding metadata.\n"
"\n"
" Example:\n"
" 'repair inobt 3' - repairs the inode btree in AG 3.\n"
" 'repair bmapbtd 128 13525' - repairs the extent map of inode 128 gen 13525.\n"
"\n"
" Known metadata repair types are:"));
for (i = 0, d = xfrog_scrubbers; i < XFS_SCRUB_TYPE_NR; i++, d++)
printf(" %s", d->name);
printf("\n");
}
static int
repair_f(
int argc,
char **argv)
{
struct xfs_scrub_metadata meta;
int error;
error = parse_args(argc, argv, &repair_cmd, &meta);
if (error)
return error;
meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR;
error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta);
if (error)
perror("repair");
if (meta.sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
printf(_("Corruption remains.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_PREEN)
printf(_("Optimization possible.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_XFAIL)
printf(_("Cross-referencing failed.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_XCORRUPT)
printf(_("Corruption still detected during cross-referencing.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE)
printf(_("Repair was not complete.\n"));
if (meta.sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED)
printf(_("Metadata did not need repair or optimization.\n"));
return 0;
}
void
repair_init(void)
{
if (!expert)
return;
repair_cmd.name = "repair";
repair_cmd.altname = "fix";
repair_cmd.cfunc = repair_f;
repair_cmd.argmin = 1;
repair_cmd.argmax = -1;
repair_cmd.flags = CMD_NOMAP_OK;
repair_cmd.args = _("type [agno|ino gen]");
repair_cmd.oneline = _("repairs filesystem metadata");
repair_cmd.help = repair_help;
add_command(&repair_cmd);
}