From: Darrick J. Wong <djwong@xxxxxxxxxx> In systemd service mode, we make systemd bind-mount the target mountpoint onto /tmp/scrub (/tmp is private to the service) so that updates to the global mountpoint in the shared mount namespace don't propagate into our service container and vice versa, and pass the path to the bind mount to xfs_scrub via -M. This solves races such as unmounting of the target mount point after service container creation but before process invocation that result in the wrong filesystem being scanned. IOWs, to scrub /usr, systemd runs "xfs_scrub -M /tmp/scrub /usr". Pretend that /usr is a separate filesystem. However, when xfs_scrub snapshots the handle of /tmp/scrub, libhandle remembers that /tmp/scrub the beginning of the path, not the pathname that we want to use for reporting (/usr). This means that handle_to_path returns /tmp/scrub and not /usr as well, with the unfortunate result that file corrupts are reported with the pathnames in the xfs_scrub@ service container, not the global ones. Put another way, xfs_scrub should complain that /usr/bin/X is corrupt, not /tmp/scrub/bin/X. Therefore, modify scrub_render_ino_descr to manipulate the path buffer during error reporting so that the user always gets the mountpoint passed in, even if someone tells us to use another path for the actual open() call in phase 1. Cc: <linux-xfs@xxxxxxxxxxxxxxx> # v6.10.0 Fixes: 9a8b09762f9a52 ("xfs_scrub: use parent pointers when possible to report file operations") Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- scrub/common.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/scrub/common.c b/scrub/common.c index 2b2d4a67bc47a2..14cd677ba5e9c4 100644 --- a/scrub/common.c +++ b/scrub/common.c @@ -418,15 +418,59 @@ scrub_render_ino_descr( if (ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT) { struct xfs_handle handle; + char *pathbuf = buf; + size_t used = 0; handle_from_fshandle(&handle, ctx->fshandle, ctx->fshandle_len); handle_from_inogen(&handle, ino, gen); + /* + * @actual_mntpoint is the path we used to open the filesystem, + * and @mntpoint is the path we use for display purposes. If + * these aren't the same string, then for reporting purposes + * we must fix the start of the path string. Start by copying + * the display mountpoint into buf, except for trailing + * slashes. At this point buf will not be null-terminated. + */ + if (ctx->actual_mntpoint != ctx->mntpoint) { + used = strlen(ctx->mntpoint); + while (used && ctx->mntpoint[used - 1] == '/') + used--; + + /* If it doesn't fit, report the handle instead. */ + if (used >= buflen) { + used = 0; + goto report_inum; + } + + memcpy(buf, ctx->mntpoint, used); + pathbuf += used; + } + ret = handle_to_path(&handle, sizeof(struct xfs_handle), 4096, - buf, buflen); + pathbuf, buflen - used); if (ret) goto report_inum; + /* + * Now that handle_to_path formatted the full path (including + * the actual mount point, stripped of any trailing slashes) + * into the rest of pathbuf, slide down the contents by the + * length of the actual mount point. Don't count any trailing + * slashes because handle_to_path uses libhandle, which strips + * trailing slashes. Copy one more byte to ensure we get the + * terminating null. + */ + if (ctx->actual_mntpoint != ctx->mntpoint) { + size_t len = strlen(ctx->actual_mntpoint); + + while (len && ctx->actual_mntpoint[len - 1] == '/') + len--; + + pathlen = strlen(pathbuf); + memmove(pathbuf, pathbuf + len, pathlen - len + 1); + } + /* * Leave at least 16 bytes for the description of what went * wrong. If we can't do that, we'll use the inode number.