Skip to content

Commit 3d2c341

Browse files
author
Darrick J. Wong
committed
xfs: scrub metadata directories
Teach online scrub about the metadata directory tree. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 5dab2da commit 3d2c341

File tree

6 files changed

+56
-3
lines changed

6 files changed

+56
-3
lines changed

fs/xfs/scrub/dir.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ xchk_dir_check_ftype(
100100

101101
if (xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype)
102102
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
103+
104+
/*
105+
* Metadata and regular inodes cannot cross trees. This property
106+
* cannot change without a full inode free and realloc cycle, so it's
107+
* safe to check this without holding locks.
108+
*/
109+
if (xfs_is_metadir_inode(ip) != xfs_is_metadir_inode(sc->ip))
110+
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
103111
}
104112

105113
/*

fs/xfs/scrub/dir_repair.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,12 @@ xrep_dir_salvage_entry(
415415
if (error)
416416
return 0;
417417

418+
/* Don't mix metadata and regular directory trees. */
419+
if (xfs_is_metadir_inode(ip) != xfs_is_metadir_inode(rd->sc->ip)) {
420+
xchk_irele(sc, ip);
421+
return 0;
422+
}
423+
418424
xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
419425
xchk_irele(sc, ip);
420426

fs/xfs/scrub/dirtree.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,8 @@ xchk_dirpath_set_outcome(
362362
STATIC int
363363
xchk_dirpath_step_up(
364364
struct xchk_dirtree *dl,
365-
struct xchk_dirpath *path)
365+
struct xchk_dirpath *path,
366+
bool is_metadir)
366367
{
367368
struct xfs_scrub *sc = dl->sc;
368369
struct xfs_inode *dp;
@@ -435,6 +436,14 @@ xchk_dirpath_step_up(
435436
goto out_scanlock;
436437
}
437438

439+
/* Parent must be in the same directory tree. */
440+
if (is_metadir != xfs_is_metadir_inode(dp)) {
441+
trace_xchk_dirpath_crosses_tree(dl->sc, dp, path->path_nr,
442+
path->nr_steps, &dl->xname, &dl->pptr_rec);
443+
error = -EFSCORRUPTED;
444+
goto out_scanlock;
445+
}
446+
438447
/*
439448
* If the extended attributes look as though they has been zapped by
440449
* the inode record repair code, we cannot scan for parent pointers.
@@ -508,6 +517,7 @@ xchk_dirpath_walk_upwards(
508517
struct xchk_dirpath *path)
509518
{
510519
struct xfs_scrub *sc = dl->sc;
520+
bool is_metadir;
511521
int error;
512522

513523
ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
@@ -538,6 +548,7 @@ xchk_dirpath_walk_upwards(
538548
* ILOCK state is no longer tracked in the scrub context. Hence we
539549
* must drop @sc->ip's ILOCK during the walk.
540550
*/
551+
is_metadir = xfs_is_metadir_inode(sc->ip);
541552
mutex_unlock(&dl->lock);
542553
xchk_iunlock(sc, XFS_ILOCK_EXCL);
543554

@@ -547,7 +558,7 @@ xchk_dirpath_walk_upwards(
547558
* If we see any kind of error here (including corruptions), the parent
548559
* pointer of @sc->ip is corrupt. Stop the whole scan.
549560
*/
550-
error = xchk_dirpath_step_up(dl, path);
561+
error = xchk_dirpath_step_up(dl, path, is_metadir);
551562
if (error) {
552563
xchk_ilock(sc, XFS_ILOCK_EXCL);
553564
mutex_lock(&dl->lock);
@@ -560,7 +571,7 @@ xchk_dirpath_walk_upwards(
560571
* *somewhere* in the path, but we don't need to stop scanning.
561572
*/
562573
while (!error && path->outcome == XCHK_DIRPATH_SCANNING)
563-
error = xchk_dirpath_step_up(dl, path);
574+
error = xchk_dirpath_step_up(dl, path, is_metadir);
564575

565576
/* Retake the locks we had, mark paths, etc. */
566577
xchk_ilock(sc, XFS_ILOCK_EXCL);

fs/xfs/scrub/findparent.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ xrep_findparent_walk_directory(
172172
*/
173173
lock_mode = xfs_ilock_data_map_shared(dp);
174174

175+
/* Don't mix metadata and regular directory trees. */
176+
if (xfs_is_metadir_inode(dp) != xfs_is_metadir_inode(sc->ip))
177+
goto out_unlock;
178+
175179
/*
176180
* If this directory is known to be sick, we cannot scan it reliably
177181
* and must abort.
@@ -368,6 +372,12 @@ xrep_findparent_confirm(
368372
return 0;
369373
}
370374

375+
/* The metadata root directory always points to itself. */
376+
if (sc->ip == sc->mp->m_metadirip) {
377+
*parent_ino = sc->mp->m_sb.sb_metadirino;
378+
return 0;
379+
}
380+
371381
/* Unlinked dirs can point anywhere; point them up to the root dir. */
372382
if (VFS_I(sc->ip)->i_nlink == 0) {
373383
*parent_ino = xchk_inode_rootdir_inum(sc->ip);
@@ -415,6 +425,9 @@ xrep_findparent_self_reference(
415425
if (sc->ip->i_ino == sc->mp->m_sb.sb_rootino)
416426
return sc->mp->m_sb.sb_rootino;
417427

428+
if (sc->ip->i_ino == sc->mp->m_sb.sb_metadirino)
429+
return sc->mp->m_sb.sb_metadirino;
430+
418431
if (VFS_I(sc->ip)->i_nlink == 0)
419432
return xchk_inode_rootdir_inum(sc->ip);
420433

fs/xfs/scrub/parent.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ xchk_parent_validate(
132132
return 0;
133133
}
134134

135+
/* Is this the metadata root dir? Then '..' must point to itself. */
136+
if (sc->ip == mp->m_metadirip) {
137+
if (sc->ip->i_ino != mp->m_sb.sb_metadirino ||
138+
sc->ip->i_ino != parent_ino)
139+
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
140+
return 0;
141+
}
142+
135143
/* '..' must not point to ourselves. */
136144
if (sc->ip->i_ino == parent_ino) {
137145
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
@@ -185,6 +193,12 @@ xchk_parent_validate(
185193
goto out_unlock;
186194
}
187195

196+
/* Metadata and regular inodes cannot cross trees. */
197+
if (xfs_is_metadir_inode(dp) != xfs_is_metadir_inode(sc->ip)) {
198+
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
199+
goto out_unlock;
200+
}
201+
188202
/* Look for a directory entry in the parent pointing to the child. */
189203
error = xchk_dir_walk(sc, dp, xchk_parent_actor, &spc);
190204
if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))

fs/xfs/scrub/trace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,6 +1753,7 @@ DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_badgen);
17531753
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_nondir_parent);
17541754
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_unlinked_parent);
17551755
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_found_next_step);
1756+
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_crosses_tree);
17561757

17571758
TRACE_DEFINE_ENUM(XCHK_DIRPATH_SCANNING);
17581759
TRACE_DEFINE_ENUM(XCHK_DIRPATH_DELETE);

0 commit comments

Comments
 (0)