mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
xfs: check metadata directory file path connectivity
Create a new scrubber type that checks that well known metadata directory paths are connected to the metadata inode that the incore structures think is in use. For example, check that "/quota/user" in the metadata directory tree actually points to mp->m_quotainfo->qi_uquotaip->i_ino. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
9dc31acb01
commit
b3c03efa59
@ -174,6 +174,7 @@ xfs-y += $(addprefix scrub/, \
|
||||
inode.o \
|
||||
iscan.o \
|
||||
listxattr.o \
|
||||
metapath.o \
|
||||
nlinks.o \
|
||||
parent.o \
|
||||
readdir.o \
|
||||
|
@ -199,6 +199,7 @@ struct xfs_fsop_geom {
|
||||
#define XFS_FSOP_GEOM_SICK_QUOTACHECK (1 << 6) /* quota counts */
|
||||
#define XFS_FSOP_GEOM_SICK_NLINKS (1 << 7) /* inode link counts */
|
||||
#define XFS_FSOP_GEOM_SICK_METADIR (1 << 8) /* metadata directory */
|
||||
#define XFS_FSOP_GEOM_SICK_METAPATH (1 << 9) /* metadir tree path */
|
||||
|
||||
/* Output for XFS_FS_COUNTS */
|
||||
typedef struct xfs_fsop_counts {
|
||||
@ -732,9 +733,10 @@ struct xfs_scrub_metadata {
|
||||
#define XFS_SCRUB_TYPE_NLINKS 26 /* inode link counts */
|
||||
#define XFS_SCRUB_TYPE_HEALTHY 27 /* everything checked out ok */
|
||||
#define XFS_SCRUB_TYPE_DIRTREE 28 /* directory tree structure */
|
||||
#define XFS_SCRUB_TYPE_METAPATH 29 /* metadata directory tree paths */
|
||||
|
||||
/* Number of scrub subcommands. */
|
||||
#define XFS_SCRUB_TYPE_NR 29
|
||||
#define XFS_SCRUB_TYPE_NR 30
|
||||
|
||||
/*
|
||||
* This special type code only applies to the vectored scrub implementation.
|
||||
@ -812,6 +814,15 @@ struct xfs_scrub_vec_head {
|
||||
|
||||
#define XFS_SCRUB_VEC_FLAGS_ALL (0)
|
||||
|
||||
/*
|
||||
* i: sm_ino values for XFS_SCRUB_TYPE_METAPATH to select a metadata file for
|
||||
* path checking.
|
||||
*/
|
||||
#define XFS_SCRUB_METAPATH_PROBE (0) /* do we have a metapath scrubber? */
|
||||
|
||||
/* Number of metapath sm_ino values */
|
||||
#define XFS_SCRUB_METAPATH_NR (1)
|
||||
|
||||
/*
|
||||
* ioctl limits
|
||||
*/
|
||||
|
@ -63,6 +63,7 @@ struct xfs_da_args;
|
||||
#define XFS_SICK_FS_QUOTACHECK (1 << 4) /* quota counts */
|
||||
#define XFS_SICK_FS_NLINKS (1 << 5) /* inode link counts */
|
||||
#define XFS_SICK_FS_METADIR (1 << 6) /* metadata directory tree */
|
||||
#define XFS_SICK_FS_METAPATH (1 << 7) /* metadata directory tree path */
|
||||
|
||||
/* Observable health issues for realtime volume metadata. */
|
||||
#define XFS_SICK_RT_BITMAP (1 << 0) /* realtime bitmap */
|
||||
@ -107,7 +108,8 @@ struct xfs_da_args;
|
||||
XFS_SICK_FS_PQUOTA | \
|
||||
XFS_SICK_FS_QUOTACHECK | \
|
||||
XFS_SICK_FS_NLINKS | \
|
||||
XFS_SICK_FS_METADIR)
|
||||
XFS_SICK_FS_METADIR | \
|
||||
XFS_SICK_FS_METAPATH)
|
||||
|
||||
#define XFS_SICK_RT_PRIMARY (XFS_SICK_RT_BITMAP | \
|
||||
XFS_SICK_RT_SUMMARY)
|
||||
|
@ -73,6 +73,7 @@ int xchk_setup_xattr(struct xfs_scrub *sc);
|
||||
int xchk_setup_symlink(struct xfs_scrub *sc);
|
||||
int xchk_setup_parent(struct xfs_scrub *sc);
|
||||
int xchk_setup_dirtree(struct xfs_scrub *sc);
|
||||
int xchk_setup_metapath(struct xfs_scrub *sc);
|
||||
#ifdef CONFIG_XFS_RT
|
||||
int xchk_setup_rtbitmap(struct xfs_scrub *sc);
|
||||
int xchk_setup_rtsummary(struct xfs_scrub *sc);
|
||||
|
@ -109,6 +109,7 @@ static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = {
|
||||
[XFS_SCRUB_TYPE_QUOTACHECK] = { XHG_FS, XFS_SICK_FS_QUOTACHECK },
|
||||
[XFS_SCRUB_TYPE_NLINKS] = { XHG_FS, XFS_SICK_FS_NLINKS },
|
||||
[XFS_SCRUB_TYPE_DIRTREE] = { XHG_INO, XFS_SICK_INO_DIRTREE },
|
||||
[XFS_SCRUB_TYPE_METAPATH] = { XHG_FS, XFS_SICK_FS_METAPATH },
|
||||
};
|
||||
|
||||
/* Return the health status mask for this scrub type. */
|
||||
|
174
fs/xfs/scrub/metapath.c
Normal file
174
fs/xfs/scrub/metapath.c
Normal file
@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023-2024 Oracle. All Rights Reserved.
|
||||
* Author: Darrick J. Wong <djwong@kernel.org>
|
||||
*/
|
||||
#include "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_metafile.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "xfs_qm.h"
|
||||
#include "xfs_dir2.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/trace.h"
|
||||
#include "scrub/readdir.h"
|
||||
|
||||
/*
|
||||
* Metadata Directory Tree Paths
|
||||
* =============================
|
||||
*
|
||||
* A filesystem with metadir enabled expects to find metadata structures
|
||||
* attached to files that are accessible by walking a path down the metadata
|
||||
* directory tree. Given the metadir path and the incore inode storing the
|
||||
* metadata, this scrubber ensures that the ondisk metadir path points to the
|
||||
* ondisk inode represented by the incore inode.
|
||||
*/
|
||||
|
||||
struct xchk_metapath {
|
||||
struct xfs_scrub *sc;
|
||||
|
||||
/* Name for lookup */
|
||||
struct xfs_name xname;
|
||||
|
||||
/* Path for this metadata file and the parent directory */
|
||||
const char *path;
|
||||
const char *parent_path;
|
||||
|
||||
/* Directory parent of the metadata file. */
|
||||
struct xfs_inode *dp;
|
||||
|
||||
/* Locks held on dp */
|
||||
unsigned int dp_ilock_flags;
|
||||
};
|
||||
|
||||
/* Release resources tracked in the buffer. */
|
||||
static inline void
|
||||
xchk_metapath_cleanup(
|
||||
void *buf)
|
||||
{
|
||||
struct xchk_metapath *mpath = buf;
|
||||
|
||||
if (mpath->dp_ilock_flags)
|
||||
xfs_iunlock(mpath->dp, mpath->dp_ilock_flags);
|
||||
kfree(mpath->path);
|
||||
}
|
||||
|
||||
int
|
||||
xchk_setup_metapath(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
if (!xfs_has_metadir(sc->mp))
|
||||
return -ENOENT;
|
||||
if (sc->sm->sm_gen)
|
||||
return -EINVAL;
|
||||
|
||||
switch (sc->sm->sm_ino) {
|
||||
case XFS_SCRUB_METAPATH_PROBE:
|
||||
/* Just probing, nothing else to do. */
|
||||
if (sc->sm->sm_agno)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Take the ILOCK on the metadata directory parent and child. We do not know
|
||||
* that the metadata directory is not corrupt, so we lock the parent and try
|
||||
* to lock the child. Returns 0 if successful, or -EINTR to abort the scrub.
|
||||
*/
|
||||
STATIC int
|
||||
xchk_metapath_ilock_both(
|
||||
struct xchk_metapath *mpath)
|
||||
{
|
||||
struct xfs_scrub *sc = mpath->sc;
|
||||
int error = 0;
|
||||
|
||||
while (true) {
|
||||
xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
|
||||
if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
|
||||
mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
|
||||
return 0;
|
||||
}
|
||||
xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
|
||||
|
||||
if (xchk_should_terminate(sc, &error))
|
||||
return error;
|
||||
|
||||
delay(1);
|
||||
}
|
||||
|
||||
ASSERT(0);
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
/* Unlock parent and child inodes. */
|
||||
static inline void
|
||||
xchk_metapath_iunlock(
|
||||
struct xchk_metapath *mpath)
|
||||
{
|
||||
struct xfs_scrub *sc = mpath->sc;
|
||||
|
||||
xchk_iunlock(sc, XFS_ILOCK_EXCL);
|
||||
|
||||
mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
|
||||
xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
|
||||
}
|
||||
|
||||
int
|
||||
xchk_metapath(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
struct xchk_metapath *mpath = sc->buf;
|
||||
xfs_ino_t ino = NULLFSINO;
|
||||
int error;
|
||||
|
||||
/* Just probing, nothing else to do. */
|
||||
if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
|
||||
return 0;
|
||||
|
||||
/* Parent required to do anything else. */
|
||||
if (mpath->dp == NULL) {
|
||||
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
|
||||
return 0;
|
||||
}
|
||||
|
||||
error = xchk_trans_alloc_empty(sc);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xchk_metapath_ilock_both(mpath);
|
||||
if (error)
|
||||
goto out_cancel;
|
||||
|
||||
/* Make sure the parent dir has a dirent pointing to this file. */
|
||||
error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
|
||||
trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
|
||||
if (error == -ENOENT) {
|
||||
/* No directory entry at all */
|
||||
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
|
||||
error = 0;
|
||||
goto out_ilock;
|
||||
}
|
||||
if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
|
||||
goto out_ilock;
|
||||
if (ino != sc->ip->i_ino) {
|
||||
/* Pointing to wrong inode */
|
||||
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
|
||||
}
|
||||
|
||||
out_ilock:
|
||||
xchk_metapath_iunlock(mpath);
|
||||
out_cancel:
|
||||
xchk_trans_cancel(sc);
|
||||
return error;
|
||||
}
|
@ -442,6 +442,13 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
|
||||
.has = xfs_has_parent,
|
||||
.repair = xrep_dirtree,
|
||||
},
|
||||
[XFS_SCRUB_TYPE_METAPATH] = { /* metadata directory tree path */
|
||||
.type = ST_GENERIC,
|
||||
.setup = xchk_setup_metapath,
|
||||
.scrub = xchk_metapath,
|
||||
.has = xfs_has_metadir,
|
||||
.repair = xrep_notsupported,
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
@ -489,6 +496,8 @@ xchk_validate_inputs(
|
||||
if (sm->sm_agno || (sm->sm_gen && !sm->sm_ino))
|
||||
goto out;
|
||||
break;
|
||||
case ST_GENERIC:
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ enum xchk_type {
|
||||
ST_PERAG, /* per-AG metadata */
|
||||
ST_FS, /* per-FS metadata */
|
||||
ST_INODE, /* per-inode metadata */
|
||||
ST_GENERIC, /* determined by the scrubber */
|
||||
};
|
||||
|
||||
struct xchk_meta_ops {
|
||||
@ -255,6 +256,7 @@ int xchk_xattr(struct xfs_scrub *sc);
|
||||
int xchk_symlink(struct xfs_scrub *sc);
|
||||
int xchk_parent(struct xfs_scrub *sc);
|
||||
int xchk_dirtree(struct xfs_scrub *sc);
|
||||
int xchk_metapath(struct xfs_scrub *sc);
|
||||
#ifdef CONFIG_XFS_RT
|
||||
int xchk_rtbitmap(struct xfs_scrub *sc);
|
||||
int xchk_rtsummary(struct xfs_scrub *sc);
|
||||
|
@ -80,6 +80,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = {
|
||||
[XFS_SCRUB_TYPE_QUOTACHECK] = "quotacheck",
|
||||
[XFS_SCRUB_TYPE_NLINKS] = "nlinks",
|
||||
[XFS_SCRUB_TYPE_DIRTREE] = "dirtree",
|
||||
[XFS_SCRUB_TYPE_METAPATH] = "metapath",
|
||||
};
|
||||
|
||||
/* Format the scrub stats into a text buffer, similar to pcp style. */
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "xfs_dir2.h"
|
||||
#include "xfs_rmap.h"
|
||||
#include "xfs_parent.h"
|
||||
#include "xfs_metafile.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/xfile.h"
|
||||
#include "scrub/xfarray.h"
|
||||
|
@ -70,6 +70,7 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_NLINKS);
|
||||
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_HEALTHY);
|
||||
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_DIRTREE);
|
||||
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_BARRIER);
|
||||
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_METAPATH);
|
||||
|
||||
#define XFS_SCRUB_TYPE_STRINGS \
|
||||
{ XFS_SCRUB_TYPE_PROBE, "probe" }, \
|
||||
@ -101,7 +102,8 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_BARRIER);
|
||||
{ XFS_SCRUB_TYPE_NLINKS, "nlinks" }, \
|
||||
{ XFS_SCRUB_TYPE_HEALTHY, "healthy" }, \
|
||||
{ XFS_SCRUB_TYPE_DIRTREE, "dirtree" }, \
|
||||
{ XFS_SCRUB_TYPE_BARRIER, "barrier" }
|
||||
{ XFS_SCRUB_TYPE_BARRIER, "barrier" }, \
|
||||
{ XFS_SCRUB_TYPE_METAPATH, "metapath" }
|
||||
|
||||
#define XFS_SCRUB_FLAG_STRINGS \
|
||||
{ XFS_SCRUB_IFLAG_REPAIR, "repair" }, \
|
||||
@ -1916,6 +1918,38 @@ TRACE_EVENT(xchk_dirtree_live_update,
|
||||
__get_str(name))
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(xchk_metapath_class,
|
||||
TP_PROTO(struct xfs_scrub *sc, const char *path,
|
||||
struct xfs_inode *dp, xfs_ino_t ino),
|
||||
TP_ARGS(sc, path, dp, ino),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_ino_t, scrub_ino)
|
||||
__field(xfs_ino_t, parent_ino)
|
||||
__field(xfs_ino_t, ino)
|
||||
__string(name, path)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = sc->mp->m_super->s_dev;
|
||||
__entry->scrub_ino = sc->ip ? sc->ip->i_ino : NULLFSINO;
|
||||
__entry->parent_ino = dp ? dp->i_ino : NULLFSINO;
|
||||
__entry->ino = ino;
|
||||
__assign_str(name);
|
||||
),
|
||||
TP_printk("dev %d:%d ino 0x%llx parent_ino 0x%llx name '%s' ino 0x%llx",
|
||||
MAJOR(__entry->dev), MINOR(__entry->dev),
|
||||
__entry->scrub_ino,
|
||||
__entry->parent_ino,
|
||||
__get_str(name),
|
||||
__entry->ino)
|
||||
);
|
||||
#define DEFINE_XCHK_METAPATH_EVENT(name) \
|
||||
DEFINE_EVENT(xchk_metapath_class, name, \
|
||||
TP_PROTO(struct xfs_scrub *sc, const char *path, \
|
||||
struct xfs_inode *dp, xfs_ino_t ino), \
|
||||
TP_ARGS(sc, path, dp, ino))
|
||||
DEFINE_XCHK_METAPATH_EVENT(xchk_metapath_lookup);
|
||||
|
||||
/* repair tracepoints */
|
||||
#if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR)
|
||||
|
||||
|
@ -381,6 +381,7 @@ static const struct ioctl_sick_map fs_map[] = {
|
||||
{ XFS_SICK_FS_QUOTACHECK, XFS_FSOP_GEOM_SICK_QUOTACHECK },
|
||||
{ XFS_SICK_FS_NLINKS, XFS_FSOP_GEOM_SICK_NLINKS },
|
||||
{ XFS_SICK_FS_METADIR, XFS_FSOP_GEOM_SICK_METADIR },
|
||||
{ XFS_SICK_FS_METAPATH, XFS_FSOP_GEOM_SICK_METAPATH },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user