xfs: report nonexistent parents as a filesystem corruption

LOLLM noticed that when the directory tree scrubber tries to walk up a
parent pointer but the parent inumber doesn't point to an allocated
inode, we allow the EINVAL/ENOENT error code to bubble up to userspace.
That's not right, we should be reporting that as a cross-referencing
error so that someone runs the parent pointer checker.

Also add a termination check to xchk_dirpath_step_up because it's a loop
body function.

Cc: stable@vger.kernel.org # v6.10
Fixes: 928b721a11 ("xfs: teach online scrub to find directory tree structure problems")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
Darrick J. Wong 2026-09-01 22:48:14 -07:00 committed by Carlos Maiolino
parent 295f2cfd3e
commit 014c1aff60
2 changed files with 61 additions and 2 deletions

View File

@ -368,12 +368,38 @@ xchk_dirpath_step_up(
struct xfs_inode *dp;
xfs_ino_t parent_ino = be64_to_cpu(dl->pptr_rec.p_ino);
unsigned int lock_mode;
int error;
int error = 0;
if (xchk_should_terminate(sc, &error))
return error;
/* Grab and lock the parent directory. */
error = xchk_iget(sc, parent_ino, &dp);
if (error)
switch (error) {
case -EINVAL:
case -ENOENT:
mutex_lock(&dl->lock);
if (dl->stale) {
/* live update detected a change in this path */
error = -ESTALE;
} else {
/* inode doesn't exist, path invalid */
error = -EFSCORRUPTED;
trace_xchk_dirpath_badino(dl->sc, path->path_nr,
path->nr_steps, &dl->xname,
&dl->pptr_rec);
}
mutex_unlock(&dl->lock);
return error;
case 0:
/* keep going */
break;
default:
return error;
}
lock_mode = xfs_ilock_attr_map_shared(dp);
mutex_lock(&dl->lock);

View File

@ -1706,6 +1706,39 @@ DEFINE_EVENT(xchk_dirtree_class, name, \
DEFINE_XCHK_DIRTREE_EVENT(xchk_dirtree_create_path);
DEFINE_XCHK_DIRTREE_EVENT(xchk_dirpath_walk_upwards);
TRACE_EVENT(xchk_dirpath_badino,
TP_PROTO(struct xfs_scrub *sc, unsigned int path_nr,
unsigned int step_nr, const struct xfs_name *name,
const struct xfs_parent_rec *pptr),
TP_ARGS(sc, path_nr, step_nr, name, pptr),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(unsigned int, path_nr)
__field(unsigned int, step_nr)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__field(unsigned int, namelen)
__dynamic_array(char, name, name->len)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->path_nr = path_nr;
__entry->step_nr = step_nr;
__entry->parent_ino = be64_to_cpu(pptr->p_ino);
__entry->parent_gen = be32_to_cpu(pptr->p_gen);
__entry->namelen = name->len;
memcpy(__get_str(name), name->name, name->len);
),
TP_printk("dev %d:%d path %u step %u parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->path_nr,
__entry->step_nr,
__entry->parent_ino,
__entry->parent_gen,
__entry->namelen,
__get_str(name))
);
DECLARE_EVENT_CLASS(xchk_dirpath_class,
TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip,
unsigned int path_nr, unsigned int step_nr,