]> Cypherpunks repositories - gostls13.git/commitdiff
internal/fsys: follow root symlink in fsys.Walk
authorMichael Matloob <matloob@golang.org>
Mon, 7 Nov 2022 19:30:45 +0000 (14:30 -0500)
committerMichael Matloob <matloob@golang.org>
Wed, 9 Nov 2022 18:07:37 +0000 (18:07 +0000)
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 <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/go/internal/fsys/fsys.go
src/cmd/go/internal/fsys/fsys_test.go
src/cmd/go/testdata/script/list_symlink_dotdotdot.txt [new file with mode: 0644]

index b5754f40caf5141c1d6d7f9213102e21e26f9d7f..311e0339303e97ddcd5b29fed77ff3d6378cb420 100644 (file)
@@ -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
index b441e19afefcaf3c29efd1e9d784d27bc3bb0637..deb63f22e622be24a17b40de50d7c06f06db5de8 100644 (file)
@@ -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 (file)
index 0000000..8df1982
--- /dev/null
@@ -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