From 4f13067f8ac7f2c64a093ef4125cfa03dd67a0a5 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Mon, 7 Nov 2022 14:30:45 -0500 Subject: [PATCH] internal/fsys: follow root symlink in fsys.Walk If fsys.Walk is called with a root directory that is a symlink, follow the symlink when doing the walk. This allows for users setting their current directory to a symlink to a module. Fixes #50807 Change-Id: Ie65a7cb804b87dea632ea6c758c20adcfa62fcd4 Reviewed-on: https://go-review.googlesource.com/c/go/+/448360 Reviewed-by: Bryan Mills Run-TryBot: Bryan Mills TryBot-Result: Gopher Robot --- src/cmd/go/internal/fsys/fsys.go | 28 ++++++++++++++++--- src/cmd/go/internal/fsys/fsys_test.go | 4 +-- .../script/list_symlink_dotdotdot.txt | 20 +++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 src/cmd/go/testdata/script/list_symlink_dotdotdot.txt diff --git a/src/cmd/go/internal/fsys/fsys.go b/src/cmd/go/internal/fsys/fsys.go index b5754f40ca..311e033930 100644 --- a/src/cmd/go/internal/fsys/fsys.go +++ b/src/cmd/go/internal/fsys/fsys.go @@ -474,19 +474,23 @@ func IsDirWithGoFiles(dir string) (bool, error) { // walk recursively descends path, calling walkFn. Copied, with some // modifications from path/filepath.walk. -func walk(path string, info fs.FileInfo, walkFn filepath.WalkFunc) error { +// Walk follows the root if it's a symlink, but reports the original paths, +// so it calls walk with both the resolvedPath (which is the path with the root resolved) +// and path (which is the path reported to the walkFn). +func walk(path, resolvedPath string, info fs.FileInfo, walkFn filepath.WalkFunc) error { if err := walkFn(path, info, nil); err != nil || !info.IsDir() { return err } - fis, err := ReadDir(path) + fis, err := ReadDir(resolvedPath) if err != nil { return walkFn(path, info, err) } for _, fi := range fis { filename := filepath.Join(path, fi.Name()) - if err := walk(filename, fi, walkFn); err != nil { + resolvedFilename := filepath.Join(resolvedPath, fi.Name()) + if err := walk(filename, resolvedFilename, fi, walkFn); err != nil { if !fi.IsDir() || err != filepath.SkipDir { return err } @@ -503,7 +507,23 @@ func Walk(root string, walkFn filepath.WalkFunc) error { if err != nil { err = walkFn(root, nil, err) } else { - err = walk(root, info, walkFn) + resolved := root + if info.Mode()&os.ModeSymlink != 0 { + // Walk follows root if it's a symlink (but does not follow other symlinks). + if op, ok := OverlayPath(root); ok { + resolved = op + } + resolved, err = os.Readlink(resolved) + if err != nil { + return err + } + // Re-stat to get the info for the resolved file. + info, err = Lstat(resolved) + if err != nil { + return err + } + } + err = walk(root, resolved, info, walkFn) } if err == filepath.SkipDir { return nil diff --git a/src/cmd/go/internal/fsys/fsys_test.go b/src/cmd/go/internal/fsys/fsys_test.go index b441e19afe..deb63f22e6 100644 --- a/src/cmd/go/internal/fsys/fsys_test.go +++ b/src/cmd/go/internal/fsys/fsys_test.go @@ -844,8 +844,8 @@ func TestWalkSymlink(t *testing.T) { {"control", "dir", []string{"dir", "dir" + string(filepath.Separator) + "file"}}, // ensure Walk doesn't walk into the directory pointed to by the symlink // (because it's supposed to use Lstat instead of Stat). - {"symlink_to_dir", "symlink", []string{"symlink"}}, - {"overlay_to_symlink_to_dir", "overlay_symlink", []string{"overlay_symlink"}}, + {"symlink_to_dir", "symlink", []string{"symlink", "symlink" + string(filepath.Separator) + "file"}}, + {"overlay_to_symlink_to_dir", "overlay_symlink", []string{"overlay_symlink", "overlay_symlink" + string(filepath.Separator) + "file"}}, } for _, tc := range testCases { diff --git a/src/cmd/go/testdata/script/list_symlink_dotdotdot.txt b/src/cmd/go/testdata/script/list_symlink_dotdotdot.txt new file mode 100644 index 0000000000..8df1982484 --- /dev/null +++ b/src/cmd/go/testdata/script/list_symlink_dotdotdot.txt @@ -0,0 +1,20 @@ +[!symlink] skip + +symlink $WORK/gopath/src/sym -> $WORK/gopath/src/tree +symlink $WORK/gopath/src/tree/squirrel -> $WORK/gopath/src/dir2 # this symlink should not be followed +cd sym +go list ./... +cmp stdout $WORK/gopath/src/want_list.txt +-- tree/go.mod -- +module example.com/tree + +go 1.20 +-- tree/tree.go -- +package tree +-- tree/branch/branch.go -- +package branch +-- dir2/squirrel.go -- +package squirrel +-- want_list.txt -- +example.com/tree +example.com/tree/branch -- 2.51.0