From: Darrick J. Wong Date: Sun, 22 Feb 2026 22:41:14 +0000 (-0800) Subject: xfs_healer: use getparents to look up file names X-Git-Tag: v7.0.0~45 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=17d840245c0e2063993eca4eb3671a576454b0ac;p=xfsprogs-dev.git xfs_healer: use getparents to look up file names 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" Reviewed-by: Christoph Hellwig --- diff --git a/healer/fsrepair.c b/healer/fsrepair.c index 907afca3..4534104f 100644 --- a/healer/fsrepair.c +++ b/healer/fsrepair.c @@ -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); diff --git a/healer/weakhandle.c b/healer/weakhandle.c index 53df43b0..8950e0eb 100644 --- a/healer/weakhandle.c +++ b/healer/weakhandle.c @@ -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; +} diff --git a/healer/xfs_healer.c b/healer/xfs_healer.c index 45ec4f28..71afa033 100644 --- a/healer/xfs_healer.c +++ b/healer/xfs_healer.c @@ -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) { diff --git a/healer/xfs_healer.h b/healer/xfs_healer.h index a4de1ad3..6d129212 100644 --- a/healer/xfs_healer.h +++ b/healer/xfs_healer.h @@ -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_ */