]> git-server-git.apps.pok.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs_healer: use getparents to look up file names
authorDarrick J. Wong <djwong@kernel.org>
Sun, 22 Feb 2026 22:41:14 +0000 (14:41 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Thu, 9 Apr 2026 22:30:17 +0000 (15:30 -0700)
If the kernel tells about something that happened to a file, use the
GETPARENTS ioctl to try to look up the path to that file for more
ergonomic reporting.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
healer/fsrepair.c
healer/weakhandle.c
healer/xfs_healer.c
healer/xfs_healer.h

index 907afca3dba8a7a9ef265323cd9ebe33d7c63fbb..4534104f8a6ac14293e9089150e46e7f46a51472 100644 (file)
@@ -164,7 +164,7 @@ try_repair_rtgroup(
 static void
 try_repair_inode(
        struct healer_ctx                       *ctx,
-       const struct hme_prefix                 *pfx,
+       const struct hme_prefix                 *orig_pfx,
        int                                     mnt_fd,
        const struct xfs_health_monitor_event   *hme)
 {
@@ -182,13 +182,25 @@ try_repair_inode(
                {0,             0},
        };
 #undef X
-       const struct u32_scrub *f;
+       struct hme_prefix       new_pfx;
+       const struct hme_prefix *pfx = orig_pfx;
+       const struct u32_scrub  *f;
 
        foreach_scrub_type(f, hme->e.inode.mask, INODE_STRUCTURES) {
                enum repair_outcome     outcome =
                        xfs_repair_metadata(mnt_fd, f->scrub_type,
                                        0, hme->e.inode.ino, hme->e.inode.gen);
 
+               /*
+                * Try again to find the file path, maybe we fixed the dir
+                * tree.
+                */
+               if (!hme_prefix_has_path(pfx)) {
+                       lookup_path(ctx, hme, &new_pfx);
+                       if (hme_prefix_has_path(&new_pfx))
+                               pfx = &new_pfx;
+               }
+
                pthread_mutex_lock(&ctx->conlock);
                report_health_repair(pfx, hme, f->event_mask, outcome);
                pthread_mutex_unlock(&ctx->conlock);
index 53df43b03e16cc6385fb9ebff82a0e0b8ac4bb35..8950e0eb1e5a43e56c9fef56d6795765b28a3444 100644 (file)
@@ -11,6 +11,8 @@
 #include "handle.h"
 #include "libfrog/fsgeom.h"
 #include "libfrog/workqueue.h"
+#include "libfrog/getparents.h"
+#include "libfrog/paths.h"
 #include "xfs_healer.h"
 
 struct weakhandle {
@@ -113,3 +115,87 @@ weakhandle_free(
 
        *whp = NULL;
 }
+
+struct bufvec {
+       char    *buf;
+       size_t  len;
+};
+
+static int
+render_path(
+       const char              *mntpt,
+       const struct path_list  *path,
+       void                    *arg)
+{
+       struct bufvec           *args = arg;
+       int                     mntpt_len = strlen(mntpt);
+       ssize_t                 ret;
+
+       /* Trim trailing slashes from the mountpoint */
+       while (mntpt_len > 0 && mntpt[mntpt_len - 1] == '/')
+               mntpt_len--;
+
+       ret = snprintf(args->buf, args->len, "%.*s", mntpt_len, mntpt);
+       if (ret < 0 || ret >= args->len)
+               return 0;
+
+       ret = path_list_to_string(path, args->buf + ret, args->len - ret);
+       if (ret < 0)
+               return 0;
+
+       /* magic code that means we found one */
+       return ECANCELED;
+}
+
+/* Render any path to this weakhandle into the specified buffer. */
+int
+weakhandle_getpath_for(
+       struct weakhandle       *wh,
+       uint64_t                ino,
+       uint32_t                gen,
+       char                    *path,
+       size_t                  pathlen)
+{
+       struct xfs_handle       fakehandle;
+       struct bufvec           bv = {
+               .buf            = path,
+               .len            = pathlen,
+       };
+       int                     mnt_fd;
+       int                     ret;
+
+       if (wh->hlen != sizeof(fakehandle)) {
+               errno = EINVAL;
+               return -1;
+       }
+       memcpy(&fakehandle, wh->hanp, sizeof(fakehandle));
+       fakehandle.ha_fid.fid_ino = ino;
+       fakehandle.ha_fid.fid_gen = gen;
+
+       ret = weakhandle_reopen(wh, &mnt_fd);
+       if (ret)
+               return ret;
+
+       /*
+        * In the common case, files only have one parent; and what's the
+        * chance that we'll need to walk past the second parent to find *one*
+        * path that goes to the rootdir?  With a max filename length of 255
+        * bytes, we pick 600 for the buffer size.
+        */
+       ret = handle_walk_paths_fd(wh->mntpoint, mnt_fd, &fakehandle,
+                       sizeof(fakehandle), 600, render_path, &bv);
+       switch (ret) {
+       case ECANCELED:
+               /* found a path */
+               ret = 0;
+               break;
+       default:
+               /* didn't find one */
+               errno = ENOENT;
+               ret = -1;
+               break;
+       }
+
+       close(mnt_fd);
+       return ret;
+}
index 45ec4f28f180d7753072df5ac4be1ada7dfb2110..71afa0331c8c7e29d78c12e30d1a1cf921e6e69c 100644 (file)
@@ -34,6 +34,39 @@ open_health_monitor(
        return ioctl(mnt_fd, XFS_IOC_HEALTH_MONITOR, &hmo);
 }
 
+/* Report either the file handle or its path, if we can. */
+void
+lookup_path(
+       struct healer_ctx                       *ctx,
+       const struct xfs_health_monitor_event   *hme,
+       struct hme_prefix                       *pfx)
+{
+       uint64_t                                ino = 0;
+       uint32_t                                gen = 0;
+       int                                     ret;
+
+       if (!healer_has_parent(ctx))
+               return;
+
+       switch (hme->domain) {
+       case XFS_HEALTH_MONITOR_DOMAIN_INODE:
+               ino = hme->e.inode.ino;
+               gen = hme->e.inode.gen;
+               break;
+       case XFS_HEALTH_MONITOR_DOMAIN_FILERANGE:
+               ino = hme->e.filerange.ino;
+               gen = hme->e.filerange.gen;
+               break;
+       default:
+               return;
+       }
+
+       ret = weakhandle_getpath_for(ctx->wh, ino, gen, pfx->path,
+                       sizeof(pfx->path));
+       if (ret)
+               hme_prefix_clear_path(pfx);
+}
+
 /* Decide if this event can only be reported upon, and not acted upon. */
 static bool
 event_not_actionable(
@@ -90,6 +123,13 @@ handle_event(
 
        hme_prefix_init(&pfx, ctx->mntpoint);
 
+       /*
+        * Try to look up the file name for the file we're about to log or
+        * about to repair (which always logs).
+        */
+       if (loggable || will_repair)
+               lookup_path(ctx, hme, &pfx);
+
        /*
         * Non-actionable events should always be logged, because they are 100%
         * informational.
@@ -198,9 +238,10 @@ setup_monitor(
 
        /*
         * Open weak-referenced file handle to mountpoint so that we can
-        * reconnect to the mountpoint to start repairs.
+        * reconnect to the mountpoint to start repairs or to look up file
+        * paths for logging.
         */
-       if (ctx->want_repair) {
+       if (ctx->want_repair || healer_has_parent(ctx)) {
                ret = weakhandle_alloc(ctx->mnt.fd, ctx->mntpoint,
                                ctx->fsname, &ctx->wh);
                if (ret) {
index a4de1ad32a408f83d746f799b8e51552582aed4e..6d12921245934c2a5b6a0fd838975e83b5f487c0 100644 (file)
@@ -61,6 +61,10 @@ static inline bool healer_has_parent(const struct healer_ctx *ctx)
        return ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT;
 }
 
+void lookup_path(struct healer_ctx *ctx,
+               const struct xfs_health_monitor_event *hme,
+               struct hme_prefix *pfx);
+
 /* repair.c */
 int repair_metadata(struct healer_ctx *ctx, const struct hme_prefix *pfx,
                const struct xfs_health_monitor_event *hme);
@@ -71,5 +75,7 @@ int weakhandle_alloc(int fd, const char *mountpoint, const char *fsname,
                struct weakhandle **whp);
 int weakhandle_reopen(struct weakhandle *wh, int *fd);
 void weakhandle_free(struct weakhandle **whp);
+int weakhandle_getpath_for(struct weakhandle *wh, uint64_t ino, uint32_t gen,
+               char *path, size_t pathlen);
 
 #endif /* XFS_HEALER_XFS_HEALER_H_ */