Skip to content

Commit b3c03ef

Browse files
author
Darrick J. Wong
committed
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 <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 9dc31ac commit b3c03ef

File tree

12 files changed

+241
-3
lines changed

12 files changed

+241
-3
lines changed

fs/xfs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ xfs-y += $(addprefix scrub/, \
174174
inode.o \
175175
iscan.o \
176176
listxattr.o \
177+
metapath.o \
177178
nlinks.o \
178179
parent.o \
179180
readdir.o \

fs/xfs/libxfs/xfs_fs.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ struct xfs_fsop_geom {
199199
#define XFS_FSOP_GEOM_SICK_QUOTACHECK (1 << 6) /* quota counts */
200200
#define XFS_FSOP_GEOM_SICK_NLINKS (1 << 7) /* inode link counts */
201201
#define XFS_FSOP_GEOM_SICK_METADIR (1 << 8) /* metadata directory */
202+
#define XFS_FSOP_GEOM_SICK_METAPATH (1 << 9) /* metadir tree path */
202203

203204
/* Output for XFS_FS_COUNTS */
204205
typedef struct xfs_fsop_counts {
@@ -732,9 +733,10 @@ struct xfs_scrub_metadata {
732733
#define XFS_SCRUB_TYPE_NLINKS 26 /* inode link counts */
733734
#define XFS_SCRUB_TYPE_HEALTHY 27 /* everything checked out ok */
734735
#define XFS_SCRUB_TYPE_DIRTREE 28 /* directory tree structure */
736+
#define XFS_SCRUB_TYPE_METAPATH 29 /* metadata directory tree paths */
735737

736738
/* Number of scrub subcommands. */
737-
#define XFS_SCRUB_TYPE_NR 29
739+
#define XFS_SCRUB_TYPE_NR 30
738740

739741
/*
740742
* This special type code only applies to the vectored scrub implementation.
@@ -812,6 +814,15 @@ struct xfs_scrub_vec_head {
812814

813815
#define XFS_SCRUB_VEC_FLAGS_ALL (0)
814816

817+
/*
818+
* i: sm_ino values for XFS_SCRUB_TYPE_METAPATH to select a metadata file for
819+
* path checking.
820+
*/
821+
#define XFS_SCRUB_METAPATH_PROBE (0) /* do we have a metapath scrubber? */
822+
823+
/* Number of metapath sm_ino values */
824+
#define XFS_SCRUB_METAPATH_NR (1)
825+
815826
/*
816827
* ioctl limits
817828
*/

fs/xfs/libxfs/xfs_health.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct xfs_da_args;
6363
#define XFS_SICK_FS_QUOTACHECK (1 << 4) /* quota counts */
6464
#define XFS_SICK_FS_NLINKS (1 << 5) /* inode link counts */
6565
#define XFS_SICK_FS_METADIR (1 << 6) /* metadata directory tree */
66+
#define XFS_SICK_FS_METAPATH (1 << 7) /* metadata directory tree path */
6667

6768
/* Observable health issues for realtime volume metadata. */
6869
#define XFS_SICK_RT_BITMAP (1 << 0) /* realtime bitmap */
@@ -107,7 +108,8 @@ struct xfs_da_args;
107108
XFS_SICK_FS_PQUOTA | \
108109
XFS_SICK_FS_QUOTACHECK | \
109110
XFS_SICK_FS_NLINKS | \
110-
XFS_SICK_FS_METADIR)
111+
XFS_SICK_FS_METADIR | \
112+
XFS_SICK_FS_METAPATH)
111113

112114
#define XFS_SICK_RT_PRIMARY (XFS_SICK_RT_BITMAP | \
113115
XFS_SICK_RT_SUMMARY)

fs/xfs/scrub/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ int xchk_setup_xattr(struct xfs_scrub *sc);
7373
int xchk_setup_symlink(struct xfs_scrub *sc);
7474
int xchk_setup_parent(struct xfs_scrub *sc);
7575
int xchk_setup_dirtree(struct xfs_scrub *sc);
76+
int xchk_setup_metapath(struct xfs_scrub *sc);
7677
#ifdef CONFIG_XFS_RT
7778
int xchk_setup_rtbitmap(struct xfs_scrub *sc);
7879
int xchk_setup_rtsummary(struct xfs_scrub *sc);

fs/xfs/scrub/health.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = {
109109
[XFS_SCRUB_TYPE_QUOTACHECK] = { XHG_FS, XFS_SICK_FS_QUOTACHECK },
110110
[XFS_SCRUB_TYPE_NLINKS] = { XHG_FS, XFS_SICK_FS_NLINKS },
111111
[XFS_SCRUB_TYPE_DIRTREE] = { XHG_INO, XFS_SICK_INO_DIRTREE },
112+
[XFS_SCRUB_TYPE_METAPATH] = { XHG_FS, XFS_SICK_FS_METAPATH },
112113
};
113114

114115
/* Return the health status mask for this scrub type. */

fs/xfs/scrub/metapath.c

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (c) 2023-2024 Oracle. All Rights Reserved.
4+
* Author: Darrick J. Wong <[email protected]>
5+
*/
6+
#include "xfs.h"
7+
#include "xfs_fs.h"
8+
#include "xfs_shared.h"
9+
#include "xfs_format.h"
10+
#include "xfs_trans_resv.h"
11+
#include "xfs_mount.h"
12+
#include "xfs_log_format.h"
13+
#include "xfs_trans.h"
14+
#include "xfs_inode.h"
15+
#include "xfs_metafile.h"
16+
#include "xfs_quota.h"
17+
#include "xfs_qm.h"
18+
#include "xfs_dir2.h"
19+
#include "scrub/scrub.h"
20+
#include "scrub/common.h"
21+
#include "scrub/trace.h"
22+
#include "scrub/readdir.h"
23+
24+
/*
25+
* Metadata Directory Tree Paths
26+
* =============================
27+
*
28+
* A filesystem with metadir enabled expects to find metadata structures
29+
* attached to files that are accessible by walking a path down the metadata
30+
* directory tree. Given the metadir path and the incore inode storing the
31+
* metadata, this scrubber ensures that the ondisk metadir path points to the
32+
* ondisk inode represented by the incore inode.
33+
*/
34+
35+
struct xchk_metapath {
36+
struct xfs_scrub *sc;
37+
38+
/* Name for lookup */
39+
struct xfs_name xname;
40+
41+
/* Path for this metadata file and the parent directory */
42+
const char *path;
43+
const char *parent_path;
44+
45+
/* Directory parent of the metadata file. */
46+
struct xfs_inode *dp;
47+
48+
/* Locks held on dp */
49+
unsigned int dp_ilock_flags;
50+
};
51+
52+
/* Release resources tracked in the buffer. */
53+
static inline void
54+
xchk_metapath_cleanup(
55+
void *buf)
56+
{
57+
struct xchk_metapath *mpath = buf;
58+
59+
if (mpath->dp_ilock_flags)
60+
xfs_iunlock(mpath->dp, mpath->dp_ilock_flags);
61+
kfree(mpath->path);
62+
}
63+
64+
int
65+
xchk_setup_metapath(
66+
struct xfs_scrub *sc)
67+
{
68+
if (!xfs_has_metadir(sc->mp))
69+
return -ENOENT;
70+
if (sc->sm->sm_gen)
71+
return -EINVAL;
72+
73+
switch (sc->sm->sm_ino) {
74+
case XFS_SCRUB_METAPATH_PROBE:
75+
/* Just probing, nothing else to do. */
76+
if (sc->sm->sm_agno)
77+
return -EINVAL;
78+
return 0;
79+
default:
80+
return -ENOENT;
81+
}
82+
}
83+
84+
/*
85+
* Take the ILOCK on the metadata directory parent and child. We do not know
86+
* that the metadata directory is not corrupt, so we lock the parent and try
87+
* to lock the child. Returns 0 if successful, or -EINTR to abort the scrub.
88+
*/
89+
STATIC int
90+
xchk_metapath_ilock_both(
91+
struct xchk_metapath *mpath)
92+
{
93+
struct xfs_scrub *sc = mpath->sc;
94+
int error = 0;
95+
96+
while (true) {
97+
xfs_ilock(mpath->dp, XFS_ILOCK_EXCL);
98+
if (xchk_ilock_nowait(sc, XFS_ILOCK_EXCL)) {
99+
mpath->dp_ilock_flags |= XFS_ILOCK_EXCL;
100+
return 0;
101+
}
102+
xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
103+
104+
if (xchk_should_terminate(sc, &error))
105+
return error;
106+
107+
delay(1);
108+
}
109+
110+
ASSERT(0);
111+
return -EINTR;
112+
}
113+
114+
/* Unlock parent and child inodes. */
115+
static inline void
116+
xchk_metapath_iunlock(
117+
struct xchk_metapath *mpath)
118+
{
119+
struct xfs_scrub *sc = mpath->sc;
120+
121+
xchk_iunlock(sc, XFS_ILOCK_EXCL);
122+
123+
mpath->dp_ilock_flags &= ~XFS_ILOCK_EXCL;
124+
xfs_iunlock(mpath->dp, XFS_ILOCK_EXCL);
125+
}
126+
127+
int
128+
xchk_metapath(
129+
struct xfs_scrub *sc)
130+
{
131+
struct xchk_metapath *mpath = sc->buf;
132+
xfs_ino_t ino = NULLFSINO;
133+
int error;
134+
135+
/* Just probing, nothing else to do. */
136+
if (sc->sm->sm_ino == XFS_SCRUB_METAPATH_PROBE)
137+
return 0;
138+
139+
/* Parent required to do anything else. */
140+
if (mpath->dp == NULL) {
141+
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
142+
return 0;
143+
}
144+
145+
error = xchk_trans_alloc_empty(sc);
146+
if (error)
147+
return error;
148+
149+
error = xchk_metapath_ilock_both(mpath);
150+
if (error)
151+
goto out_cancel;
152+
153+
/* Make sure the parent dir has a dirent pointing to this file. */
154+
error = xchk_dir_lookup(sc, mpath->dp, &mpath->xname, &ino);
155+
trace_xchk_metapath_lookup(sc, mpath->path, mpath->dp, ino);
156+
if (error == -ENOENT) {
157+
/* No directory entry at all */
158+
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
159+
error = 0;
160+
goto out_ilock;
161+
}
162+
if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
163+
goto out_ilock;
164+
if (ino != sc->ip->i_ino) {
165+
/* Pointing to wrong inode */
166+
xchk_ino_set_corrupt(sc, sc->ip->i_ino);
167+
}
168+
169+
out_ilock:
170+
xchk_metapath_iunlock(mpath);
171+
out_cancel:
172+
xchk_trans_cancel(sc);
173+
return error;
174+
}

fs/xfs/scrub/scrub.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,13 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
442442
.has = xfs_has_parent,
443443
.repair = xrep_dirtree,
444444
},
445+
[XFS_SCRUB_TYPE_METAPATH] = { /* metadata directory tree path */
446+
.type = ST_GENERIC,
447+
.setup = xchk_setup_metapath,
448+
.scrub = xchk_metapath,
449+
.has = xfs_has_metadir,
450+
.repair = xrep_notsupported,
451+
},
445452
};
446453

447454
static int
@@ -489,6 +496,8 @@ xchk_validate_inputs(
489496
if (sm->sm_agno || (sm->sm_gen && !sm->sm_ino))
490497
goto out;
491498
break;
499+
case ST_GENERIC:
500+
break;
492501
default:
493502
goto out;
494503
}

fs/xfs/scrub/scrub.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ enum xchk_type {
7373
ST_PERAG, /* per-AG metadata */
7474
ST_FS, /* per-FS metadata */
7575
ST_INODE, /* per-inode metadata */
76+
ST_GENERIC, /* determined by the scrubber */
7677
};
7778

7879
struct xchk_meta_ops {
@@ -255,6 +256,7 @@ int xchk_xattr(struct xfs_scrub *sc);
255256
int xchk_symlink(struct xfs_scrub *sc);
256257
int xchk_parent(struct xfs_scrub *sc);
257258
int xchk_dirtree(struct xfs_scrub *sc);
259+
int xchk_metapath(struct xfs_scrub *sc);
258260
#ifdef CONFIG_XFS_RT
259261
int xchk_rtbitmap(struct xfs_scrub *sc);
260262
int xchk_rtsummary(struct xfs_scrub *sc);

fs/xfs/scrub/stats.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = {
8080
[XFS_SCRUB_TYPE_QUOTACHECK] = "quotacheck",
8181
[XFS_SCRUB_TYPE_NLINKS] = "nlinks",
8282
[XFS_SCRUB_TYPE_DIRTREE] = "dirtree",
83+
[XFS_SCRUB_TYPE_METAPATH] = "metapath",
8384
};
8485

8586
/* Format the scrub stats into a text buffer, similar to pcp style. */

fs/xfs/scrub/trace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "xfs_dir2.h"
2121
#include "xfs_rmap.h"
2222
#include "xfs_parent.h"
23+
#include "xfs_metafile.h"
2324
#include "scrub/scrub.h"
2425
#include "scrub/xfile.h"
2526
#include "scrub/xfarray.h"

0 commit comments

Comments
 (0)