fix: don't resolve symlinks when making a relative path absolute

Using realpath() in watch() caused events to be emitted under the resolved
path (e.g. /private/tmp/...) rather than the path the caller provided (e.g.
/tmp/..., where /tmp is a symlink on macOS). This broke all integration
tests on macOS since the test file paths didn't match the emitted event
paths.

For absolute paths, use them unchanged. For relative paths, join with the
current working directory without further symlink resolution. This
preserves the caller's view of the path while still satisfying the kqueue
backend's requirement for absolute paths.
This commit is contained in:
CJ van den Berg 2026-03-07 20:49:03 +01:00
parent eab9b328ba
commit 5deb3dfa8b
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9

View file

@ -75,8 +75,17 @@ pub fn deinit(self: *@This()) void {
/// all subdirectories are watched recursively and new directories created
/// inside are watched automatically.
pub fn watch(self: *@This(), path: []const u8) Error!void {
// Make the path absolute without resolving symlinks so that callers who
// pass "/tmp/foo" (where /tmp is a symlink) receive events with the same
// "/tmp/foo" prefix rather than the resolved "/private/tmp/foo" prefix.
var buf: [std.fs.max_path_bytes]u8 = undefined;
const abs_path = std.fs.cwd().realpath(path, &buf) catch return error.WatchFailed;
const abs_path: []const u8 = if (std.fs.path.isAbsolute(path))
path
else blk: {
var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
const cwd = std.fs.cwd().realpath(".", &cwd_buf) catch return error.WatchFailed;
break :blk std.fmt.bufPrint(&buf, "{s}/{s}", .{ cwd, path }) catch return error.WatchFailed;
};
try self.interceptor.backend.add_watch(self.allocator, abs_path);
if (!Backend.watches_recursively) {
recurse_watch(&self.interceptor.backend, self.allocator, abs_path);