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)
{
{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);
#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 {
*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;
+}
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(
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.
/*
* 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) {
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);
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_ */