summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2026-09-01 22:48:14 -0700
committerCarlos Maiolino <cem@kernel.org>2026-09-07 07:50:36 +0200
commit014c1aff607a839b8ddc732da919b94560ae58c2 (patch)
tree0c8de54b04d16edb5f7ea3372f903e4b04f65a7f
parent295f2cfd3e2c814c2ecd2c1d522bc31c5288e414 (diff)
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: 928b721a11789a ("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>
-rw-r--r--fs/xfs/scrub/dirtree.c30
-rw-r--r--fs/xfs/scrub/trace.h33
2 files changed, 61 insertions, 2 deletions
diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c
index b2cf6e5439d9..717cbac29562 100644
--- a/fs/xfs/scrub/dirtree.c
+++ b/fs/xfs/scrub/dirtree.c
@@ -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);
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 362c6d39e9f5..0f5adc293962 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -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,