From 6f33df755ed8ace0fe078827ba12298af73c72cc Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Wed, 18 Mar 2026 09:07:56 +0900 Subject: [PATCH] Use IgnoreDuplicateDirs to prevent duplicate directory traversal When --walker=follow is used, symlink following is now handled by fastwalk's IgnoreDuplicateDirs adapter which tracks visited directories by device+inode. This prevents the same directory from being entered more than once, avoiding effectively infinite traversal when a symlink points outside the walker root. Close #4710 --- CHANGELOG.md | 1 + src/reader.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed7df1fc..a00f450a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ CHANGELOG - With the reduced per-entry cost, the cache now has broader coverage. - fish: Improved command history (CTRL-R) (#4703) (@bitraid) - Bug fixes + - `--walker=follow` no longer visits the same directory more than once. This avoids effectively infinite traversal when a symlink points outside the walker root (#4710) - Fixed AWK tokenizer not treating a new line character as whitespace - Fixed `--{accept,with}-nth` removing trailing whitespaces with a non-default `--delimiter` - Fixed OSC8 hyperlinks being mangled when the URL contains unicode characters (#4707) diff --git a/src/reader.go b/src/reader.go index 76df4a5a..105cb093 100644 --- a/src/reader.go +++ b/src/reader.go @@ -269,7 +269,9 @@ func trimPath(path string) string { func (r *Reader) readFiles(roots []string, opts walkerOpts, ignores []string) bool { conf := fastwalk.Config{ - Follow: opts.follow, + // When opts.follow is true, symlink following and loop/duplicate + // detection is handled by IgnoreDuplicateDirs below. + Follow: false, // Use forward slashes when running a Windows binary under WSL or MSYS ToSlash: fastwalk.DefaultToSlash(), Sort: fastwalk.SortFilesFirst, @@ -339,9 +341,13 @@ func (r *Reader) readFiles(roots []string, opts walkerOpts, ignores []string) bo } return nil } + var walkFn fs.WalkDirFunc = fn + if opts.follow { + walkFn = fastwalk.IgnoreDuplicateDirs(fn) + } noerr := true for _, root := range roots { - noerr = noerr && (fastwalk.Walk(&conf, root, fn) == nil) + noerr = noerr && (fastwalk.Walk(&conf, root, walkFn) == nil) } return noerr }