]> Cypherpunks repositories - gostls13.git/commitdiff
Revert "os/exec: return error when PATH lookup would use current directory"
authorBryan Mills <bcmills@google.com>
Fri, 29 Apr 2022 21:38:40 +0000 (21:38 +0000)
committerGopher Robot <gobot@golang.org>
Fri, 29 Apr 2022 23:04:17 +0000 (23:04 +0000)
This reverts CL 381374.

Reason for revert: broke tests for x/sys/execabs.

Updates #43724.
Updates #43947.

Change-Id: I9eb3adb5728dead66dbd20f6afe1e7a77e2a26f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/403058
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>

13 files changed:
api/next/43724.txt [deleted file]
src/cmd/dist/build.go
src/cmd/dist/test.go
src/cmd/dist/util.go
src/cmd/go/testdata/script/cgo_path.txt
src/internal/execabs/execabs.go
src/internal/execabs/execabs_test.go [new file with mode: 0644]
src/os/exec/dot_test.go [deleted file]
src/os/exec/exec.go
src/os/exec/lp_plan9.go
src/os/exec/lp_unix.go
src/os/exec/lp_windows.go
src/os/exec/lp_windows_test.go

diff --git a/api/next/43724.txt b/api/next/43724.txt
deleted file mode 100644 (file)
index 1030a80..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-pkg os/exec, type Cmd struct, Err error #43724
-pkg os/exec, var ErrDot error #43724
index 87e24216aa4f0e769194c6cb9b886064ff49efab..bbaf595421d0e91e548b8642fb2cd9e117922699 100644 (file)
@@ -27,7 +27,6 @@ import (
 var (
        goarch           string
        gorootBin        string
-       gorootBinGo      string
        gohostarch       string
        gohostos         string
        goos             string
@@ -115,12 +114,6 @@ func xinit() {
        goroot = filepath.Clean(b)
        gorootBin = pathf("%s/bin", goroot)
 
-       // Don't run just 'go' because the build infrastructure
-       // runs cmd/dist inside go/bin often, and on Windows
-       // it will be found in the current directory and refuse to exec.
-       // All exec calls rewrite "go" into gorootBinGo.
-       gorootBinGo = pathf("%s/bin/go", goroot)
-
        b = os.Getenv("GOROOT_FINAL")
        if b == "" {
                b = goroot
index 2b6b1e514e74acdad9f934b36497ad30b8742214..7c8f1ea46d2dcbdd8a3f9b5412bd5935bc1126bc 100644 (file)
@@ -27,7 +27,6 @@ func cmdtest() {
        gogcflags = os.Getenv("GO_GCFLAGS")
 
        var t tester
-
        var noRebuild bool
        flag.BoolVar(&t.listMode, "list", false, "list available tests")
        flag.BoolVar(&t.rebuild, "rebuild", false, "rebuild everything first")
@@ -97,9 +96,15 @@ type distTest struct {
 func (t *tester) run() {
        timelog("start", "dist test")
 
-       os.Setenv("PATH", fmt.Sprintf("%s%c%s", gorootBin, os.PathListSeparator, os.Getenv("PATH")))
+       var exeSuffix string
+       if goos == "windows" {
+               exeSuffix = ".exe"
+       }
+       if _, err := os.Stat(filepath.Join(gorootBin, "go"+exeSuffix)); err == nil {
+               os.Setenv("PATH", fmt.Sprintf("%s%c%s", gorootBin, os.PathListSeparator, os.Getenv("PATH")))
+       }
 
-       cmd := exec.Command(gorootBinGo, "env", "CGO_ENABLED")
+       cmd := exec.Command("go", "env", "CGO_ENABLED")
        cmd.Stderr = new(bytes.Buffer)
        slurp, err := cmd.Output()
        if err != nil {
@@ -414,7 +419,7 @@ func (t *tester) registerStdTest(pkg string) {
                                args = append(args, "-run=^$")
                        }
                        args = append(args, stdMatches...)
-                       cmd := exec.Command(gorootBinGo, args...)
+                       cmd := exec.Command("go", args...)
                        cmd.Stdout = os.Stdout
                        cmd.Stderr = os.Stderr
                        return cmd.Run()
@@ -451,7 +456,7 @@ func (t *tester) registerRaceBenchTest(pkg string) {
                                args = append(args, "-bench=.*")
                        }
                        args = append(args, benchMatches...)
-                       cmd := exec.Command(gorootBinGo, args...)
+                       cmd := exec.Command("go", args...)
                        cmd.Stdout = os.Stdout
                        cmd.Stderr = os.Stderr
                        return cmd.Run()
@@ -479,7 +484,7 @@ func (t *tester) registerTests() {
        } else {
                // Use a format string to only list packages and commands that have tests.
                const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
-               cmd := exec.Command(gorootBinGo, "list", "-f", format)
+               cmd := exec.Command("go", "list", "-f", format)
                if t.race {
                        cmd.Args = append(cmd.Args, "-tags=race")
                }
@@ -614,7 +619,7 @@ func (t *tester) registerTests() {
                                        fmt.Println("skipping terminal test; stdout/stderr not terminals")
                                        return nil
                                }
-                               cmd := exec.Command(gorootBinGo, "test")
+                               cmd := exec.Command("go", "test")
                                setDir(cmd, filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153"))
                                cmd.Stdout = os.Stdout
                                cmd.Stderr = os.Stderr
@@ -998,11 +1003,7 @@ func flattenCmdline(cmdline []interface{}) (bin string, args []string) {
        }
        list = out
 
-       bin = list[0]
-       if bin == "go" {
-               bin = gorootBinGo
-       }
-       return bin, list[1:]
+       return list[0], list[1:]
 }
 
 func (t *tester) addCmd(dt *distTest, dir string, cmdline ...interface{}) *exec.Cmd {
@@ -1156,7 +1157,7 @@ func (t *tester) registerHostTest(name, heading, dir, pkg string) {
 }
 
 func (t *tester) runHostTest(dir, pkg string) error {
-       out, err := exec.Command(gorootBinGo, "env", "GOEXE", "GOTMPDIR").Output()
+       out, err := exec.Command("go", "env", "GOEXE", "GOTMPDIR").Output()
        if err != nil {
                return err
        }
index ee8ba910c7ded86d6a10ac090d9ca1e5ed160907..8856f467d5ba8e66ed4ad851ed3ea4abc9efdf8a 100644 (file)
@@ -71,11 +71,7 @@ func run(dir string, mode int, cmd ...string) string {
                errprintf("run: %s\n", strings.Join(cmd, " "))
        }
 
-       bin := cmd[0]
-       if bin == "go" {
-               bin = gorootBinGo
-       }
-       xcmd := exec.Command(bin, cmd[1:]...)
+       xcmd := exec.Command(cmd[0], cmd[1:]...)
        setDir(xcmd, dir)
        var data []byte
        var err error
index 1f84dbc5b40ce3d9d5828566d4d2f2657a9126b0..be9609e86f0ddb364737d96408c997a4896bc7c9 100644 (file)
@@ -14,7 +14,7 @@ env GOCACHE=$WORK/gocache  # Looking for compile flags, so need a clean cache.
 [windows] exists -exec p/gcc.bat p/clang.bat
 ! exists p/bug.txt
 ! go build -x
-stderr '^cgo: C compiler "(clang|gcc)" not found: exec: "(clang|gcc)": cannot run executable found relative to current directory'
+stderr '^cgo: exec (clang|gcc): (clang|gcc) resolves to executable relative to current directory \(.[/\\](clang|gcc)(.bat)?\)$'
 ! exists p/bug.txt
 
 -- go.mod --
index 5f60fbb1199d16657059c8f5b670d4ef4e26c619..9a05d971dadb9e49f632ba7622f88371e1b3a27d 100644 (file)
@@ -12,7 +12,11 @@ package execabs
 
 import (
        "context"
+       "fmt"
        "os/exec"
+       "path/filepath"
+       "reflect"
+       "unsafe"
 )
 
 var ErrNotFound = exec.ErrNotFound
@@ -23,14 +27,44 @@ type (
        ExitError = exec.ExitError
 )
 
+func relError(file, path string) error {
+       return fmt.Errorf("%s resolves to executable relative to current directory (.%c%s)", file, filepath.Separator, path)
+}
+
 func LookPath(file string) (string, error) {
-       return exec.LookPath(file)
+       path, err := exec.LookPath(file)
+       if err != nil {
+               return "", err
+       }
+       if filepath.Base(file) == file && !filepath.IsAbs(path) {
+               return "", relError(file, path)
+       }
+       return path, nil
+}
+
+func fixCmd(name string, cmd *exec.Cmd) {
+       if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) {
+               // exec.Command was called with a bare binary name and
+               // exec.LookPath returned a path which is not absolute.
+               // Set cmd.lookPathErr and clear cmd.Path so that it
+               // cannot be run.
+               lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer()))
+               if *lookPathErr == nil {
+                       *lookPathErr = relError(name, cmd.Path)
+               }
+               cmd.Path = ""
+       }
 }
 
 func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
-       return exec.CommandContext(ctx, name, arg...)
+       cmd := exec.CommandContext(ctx, name, arg...)
+       fixCmd(name, cmd)
+       return cmd
+
 }
 
 func Command(name string, arg ...string) *exec.Cmd {
-       return exec.Command(name, arg...)
+       cmd := exec.Command(name, arg...)
+       fixCmd(name, cmd)
+       return cmd
 }
diff --git a/src/internal/execabs/execabs_test.go b/src/internal/execabs/execabs_test.go
new file mode 100644 (file)
index 0000000..97a3f39
--- /dev/null
@@ -0,0 +1,103 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package execabs
+
+import (
+       "context"
+       "fmt"
+       "internal/testenv"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "runtime"
+       "testing"
+)
+
+func TestFixCmd(t *testing.T) {
+       cmd := &exec.Cmd{Path: "hello"}
+       fixCmd("hello", cmd)
+       if cmd.Path != "" {
+               t.Error("fixCmd didn't clear cmd.Path")
+       }
+       expectedErr := fmt.Sprintf("hello resolves to executable relative to current directory (.%chello)", filepath.Separator)
+       if err := cmd.Run(); err == nil {
+               t.Fatal("Command.Run didn't fail")
+       } else if err.Error() != expectedErr {
+               t.Fatalf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error())
+       }
+}
+
+func TestCommand(t *testing.T) {
+       testenv.MustHaveExec(t)
+
+       for _, cmd := range []func(string) *Cmd{
+               func(s string) *Cmd { return Command(s) },
+               func(s string) *Cmd { return CommandContext(context.Background(), s) },
+       } {
+               tmpDir := t.TempDir()
+               executable := "execabs-test"
+               if runtime.GOOS == "windows" {
+                       executable += ".exe"
+               }
+               if err := os.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil {
+                       t.Fatalf("os.WriteFile failed: %s", err)
+               }
+               cwd, err := os.Getwd()
+               if err != nil {
+                       t.Fatalf("os.Getwd failed: %s", err)
+               }
+               defer os.Chdir(cwd)
+               if err = os.Chdir(tmpDir); err != nil {
+                       t.Fatalf("os.Chdir failed: %s", err)
+               }
+               if runtime.GOOS != "windows" {
+                       // add "." to PATH so that exec.LookPath looks in the current directory on
+                       // non-windows platforms as well
+                       origPath := os.Getenv("PATH")
+                       defer os.Setenv("PATH", origPath)
+                       os.Setenv("PATH", fmt.Sprintf(".:%s", origPath))
+               }
+               expectedErr := fmt.Sprintf("execabs-test resolves to executable relative to current directory (.%c%s)", filepath.Separator, executable)
+               if err = cmd("execabs-test").Run(); err == nil {
+                       t.Fatalf("Command.Run didn't fail when exec.LookPath returned a relative path")
+               } else if err.Error() != expectedErr {
+                       t.Errorf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error())
+               }
+       }
+}
+
+func TestLookPath(t *testing.T) {
+       testenv.MustHaveExec(t)
+
+       tmpDir := t.TempDir()
+       executable := "execabs-test"
+       if runtime.GOOS == "windows" {
+               executable += ".exe"
+       }
+       if err := os.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil {
+               t.Fatalf("os.WriteFile failed: %s", err)
+       }
+       cwd, err := os.Getwd()
+       if err != nil {
+               t.Fatalf("os.Getwd failed: %s", err)
+       }
+       defer os.Chdir(cwd)
+       if err = os.Chdir(tmpDir); err != nil {
+               t.Fatalf("os.Chdir failed: %s", err)
+       }
+       if runtime.GOOS != "windows" {
+               // add "." to PATH so that exec.LookPath looks in the current directory on
+               // non-windows platforms as well
+               origPath := os.Getenv("PATH")
+               defer os.Setenv("PATH", origPath)
+               os.Setenv("PATH", fmt.Sprintf(".:%s", origPath))
+       }
+       expectedErr := fmt.Sprintf("execabs-test resolves to executable relative to current directory (.%c%s)", filepath.Separator, executable)
+       if _, err := LookPath("execabs-test"); err == nil {
+               t.Fatalf("LookPath didn't fail when finding a non-relative path")
+       } else if err.Error() != expectedErr {
+               t.Errorf("LookPath returned unexpected error: want %q, got %q", expectedErr, err.Error())
+       }
+}
diff --git a/src/os/exec/dot_test.go b/src/os/exec/dot_test.go
deleted file mode 100644 (file)
index ca6b095..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package exec_test
-
-import (
-       "errors"
-       "internal/testenv"
-       "io/ioutil"
-       "os"
-       . "os/exec"
-       "path/filepath"
-       "runtime"
-       "strings"
-       "testing"
-)
-
-func TestLookPath(t *testing.T) {
-       testenv.MustHaveExec(t)
-
-       tmpDir := filepath.Join(t.TempDir(), "testdir")
-       if err := os.Mkdir(tmpDir, 0777); err != nil {
-               t.Fatal(err)
-       }
-
-       executable := "execabs-test"
-       if runtime.GOOS == "windows" {
-               executable += ".exe"
-       }
-       if err := ioutil.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0777); err != nil {
-               t.Fatal(err)
-       }
-       cwd, err := os.Getwd()
-       if err != nil {
-               t.Fatal(err)
-       }
-       defer func() {
-               if err := os.Chdir(cwd); err != nil {
-                       panic(err)
-               }
-       }()
-       if err = os.Chdir(tmpDir); err != nil {
-               t.Fatal(err)
-       }
-       origPath := os.Getenv("PATH")
-       defer os.Setenv("PATH", origPath)
-
-       // Add "." to PATH so that exec.LookPath looks in the current directory on all systems.
-       // And try to trick it with "../testdir" too.
-       for _, dir := range []string{".", "../testdir"} {
-               os.Setenv("PATH", dir+string(filepath.ListSeparator)+origPath)
-               t.Run("PATH="+dir, func(t *testing.T) {
-                       good := dir + "/execabs-test"
-                       if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) {
-                               t.Fatalf("LookPath(%q) = %q, %v, want \"%s...\", nil", good, found, err, good)
-                       }
-                       if runtime.GOOS == "windows" {
-                               good = dir + `\execabs-test`
-                               if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) {
-                                       t.Fatalf("LookPath(%q) = %q, %v, want \"%s...\", nil", good, found, err, good)
-                               }
-                       }
-
-                       if _, err := LookPath("execabs-test"); err == nil {
-                               t.Fatalf("LookPath didn't fail when finding a non-relative path")
-                       } else if !errors.Is(err, ErrDot) {
-                               t.Fatalf("LookPath returned unexpected error: want Is ErrDot, got %q", err)
-                       }
-
-                       cmd := Command("execabs-test")
-                       if cmd.Err == nil {
-                               t.Fatalf("Command didn't fail when finding a non-relative path")
-                       } else if !errors.Is(cmd.Err, ErrDot) {
-                               t.Fatalf("Command returned unexpected error: want Is ErrDot, got %q", cmd.Err)
-                       }
-                       cmd.Err = nil
-
-                       // Clearing cmd.Err should let the execution proceed,
-                       // and it should fail because it's not a valid binary.
-                       if err := cmd.Run(); err == nil {
-                               t.Fatalf("Run did not fail: expected exec error")
-                       } else if errors.Is(err, ErrDot) {
-                               t.Fatalf("Run returned unexpected error ErrDot: want error like ENOEXEC: %q", err)
-                       }
-               })
-       }
-}
index 7b72ffece4f1028d1236d13a5d8eb52319bd3e77..91c2e003d8d58931eea1efcbb9c5a24e5e390f11 100644 (file)
 // Note that the examples in this package assume a Unix system.
 // They may not run on Windows, and they do not run in the Go Playground
 // used by golang.org and godoc.org.
-//
-// Executables in the current directory
-//
-// The functions Command and LookPath look for a program
-// in the directories listed in the current path, following the
-// conventions of the host operating system.
-// Operating systems have for decades included the current
-// directory in this search, sometimes implicitly and sometimes
-// configured explicitly that way by default.
-// Modern practice is that including the current directory
-// is usually unexpected and often leads to security problems.
-//
-// To avoid those security problems, as of Go 1.19, this package will not resolve a program
-// using an implicit or explicit path entry relative to the current directory.
-// That is, if you run exec.LookPath("go"), it will not successfully return
-// ./go on Unix nor .\go.exe on Windows, no matter how the path is configured.
-// Instead, if the usual path algorithms would result in that answer,
-// these functions return an error err satisfying errors.Is(err, ErrDot).
-//
-// For example, consider these two program snippets:
-//
-//     path, err := exec.LookPath("prog")
-//     if err != nil {
-//             log.Fatal(err)
-//     }
-//     use(path)
-//
-// and
-//
-//     cmd := exec.Command("prog")
-//     if err := cmd.Run(); err != nil {
-//             log.Fatal(err)
-//     }
-//
-// These will not find and run ./prog or .\prog.exe,
-// no matter how the current path is configured.
-//
-// Code that always wants to run a program from the current directory
-// can be rewritten to say "./prog" instead of "prog".
-//
-// Code that insists on including results from relative path entries
-// can instead override the error using an errors.Is check:
-//
-//     path, err := exec.LookPath("prog")
-//     if errors.Is(err, exec.ErrDot) {
-//             err = nil
-//     }
-//     if err != nil {
-//             log.Fatal(err)
-//     }
-//     use(path)
-//
-// and
-//
-//     cmd := exec.Command("prog")
-//     if errors.Is(cmd.Err, exec.ErrDot) {
-//             cmd.Err = nil
-//     }
-//     if err := cmd.Run(); err != nil {
-//             log.Fatal(err)
-//     }
-//
-// Before adding such overrides, make sure you understand the
-// security implications of doing so.
-// See https://go.dev/blog/path-security for more information.
 package exec
 
 import (
@@ -199,7 +134,7 @@ type Cmd struct {
        ProcessState *os.ProcessState
 
        ctx             context.Context // nil means none
-       Err             error           // LookPath error, if any.
+       lookPathErr     error           // LookPath error, if any.
        finished        bool            // when Wait was called
        childFiles      []*os.File
        closeAfterStart []io.Closer
@@ -238,7 +173,7 @@ func Command(name string, arg ...string) *Cmd {
        }
        if filepath.Base(name) == name {
                if lp, err := LookPath(name); err != nil {
-                       cmd.Err = err
+                       cmd.lookPathErr = err
                } else {
                        cmd.Path = lp
                }
@@ -265,7 +200,7 @@ func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
 // In particular, it is not suitable for use as input to a shell.
 // The output of String may vary across Go releases.
 func (c *Cmd) String() string {
-       if c.Err != nil {
+       if c.lookPathErr != nil {
                // failed to resolve path; report the original requested path (plus args)
                return strings.Join(c.Args, " ")
        }
@@ -400,7 +335,7 @@ func (c *Cmd) Run() error {
 // lookExtensions does not search PATH, instead it converts `prog` into `.\prog`.
 func lookExtensions(path, dir string) (string, error) {
        if filepath.Base(path) == path {
-               path = "." + string(filepath.Separator) + path
+               path = filepath.Join(".", path)
        }
        if dir == "" {
                return LookPath(path)
@@ -428,10 +363,10 @@ func lookExtensions(path, dir string) (string, error) {
 // The Wait method will return the exit code and release associated resources
 // once the command exits.
 func (c *Cmd) Start() error {
-       if c.Err != nil {
+       if c.lookPathErr != nil {
                c.closeDescriptors(c.closeAfterStart)
                c.closeDescriptors(c.closeAfterWait)
-               return c.Err
+               return c.lookPathErr
        }
        if runtime.GOOS == "windows" {
                lp, err := lookExtensions(c.Path, c.Dir)
@@ -910,12 +845,3 @@ func addCriticalEnv(env []string) []string {
        }
        return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))
 }
-
-// ErrDot indicates that a path lookup resolved to an executable
-// in the current directory due to ‘.’ being in the path, either
-// implicitly or explicitly. See the package documentation for details.
-//
-// Note that functions in this package do not return ErrDot directly.
-// Code should use errors.Is(err, ErrDot), not err == ErrDot,
-// to test whether a returned error err is due to this condition.
-var ErrDot = errors.New("cannot run executable found relative to current directory")
index 68224814d12c47c05f10f6ab855251cfe25fa69c..e8826a5083b72609a706e56c1d1f58813ccbe50a 100644 (file)
@@ -30,11 +30,7 @@ func findExecutable(file string) error {
 // directories named by the path environment variable.
 // If file begins with "/", "#", "./", or "../", it is tried
 // directly and the path is not consulted.
-// On success, the result is an absolute path.
-//
-// In older versions of Go, LookPath could return a path relative to the current directory.
-// As of Go 1.19, LookPath will instead return that path along with an error satisfying
-// errors.Is(err, ErrDot). See the package documentation for more details.
+// The result may be an absolute path or a path relative to the current directory.
 func LookPath(file string) (string, error) {
        // skip the path lookup for these prefixes
        skip := []string{"/", "#", "./", "../"}
@@ -53,9 +49,6 @@ func LookPath(file string) (string, error) {
        for _, dir := range filepath.SplitList(path) {
                path := filepath.Join(dir, file)
                if err := findExecutable(path); err == nil {
-                       if !filepath.IsAbs(path) {
-                               return path, &Error{file, ErrDot}
-                       }
                        return path, nil
                }
        }
index 983320566366c237bd0b84696684be30b23447a1..5db6c5e1098afc785e47130282e4e0a07cb6827b 100644 (file)
@@ -31,11 +31,7 @@ func findExecutable(file string) error {
 // LookPath searches for an executable named file in the
 // directories named by the PATH environment variable.
 // If file contains a slash, it is tried directly and the PATH is not consulted.
-// Otherwise, on success, the result is an absolute path.
-//
-// In older versions of Go, LookPath could return a path relative to the current directory.
-// As of Go 1.19, LookPath will instead return that path along with an error satisfying
-// errors.Is(err, ErrDot). See the package documentation for more details.
+// The result may be an absolute path or a path relative to the current directory.
 func LookPath(file string) (string, error) {
        // NOTE(rsc): I wish we could use the Plan 9 behavior here
        // (only bypass the path if file begins with / or ./ or ../)
@@ -56,9 +52,6 @@ func LookPath(file string) (string, error) {
                }
                path := filepath.Join(dir, file)
                if err := findExecutable(path); err == nil {
-                       if !filepath.IsAbs(path) {
-                               return path, &Error{file, ErrDot}
-                       }
                        return path, nil
                }
        }
index dab57702983f18c1dacce183102a26f7d8c1aac1..e7a2cdf142a02d7cbbb551b39a9a301c84f66c15 100644 (file)
@@ -10,7 +10,6 @@ import (
        "os"
        "path/filepath"
        "strings"
-       "syscall"
 )
 
 // ErrNotFound is the error resulting if a path search failed to find an executable file.
@@ -54,14 +53,10 @@ func findExecutable(file string, exts []string) (string, error) {
 
 // LookPath searches for an executable named file in the
 // directories named by the PATH environment variable.
+// If file contains a slash, it is tried directly and the PATH is not consulted.
 // LookPath also uses PATHEXT environment variable to match
 // a suitable candidate.
-// If file contains a slash, it is tried directly and the PATH is not consulted.
-// Otherwise, on success, the result is an absolute path.
-//
-// In older versions of Go, LookPath could return a path relative to the current directory.
-// As of Go 1.19, LookPath will instead return that path along with an error satisfying
-// errors.Is(err, ErrDot). See the package documentation for more details.
+// The result may be an absolute path or a path relative to the current directory.
 func LookPath(file string) (string, error) {
        var exts []string
        x := os.Getenv(`PATHEXT`)
@@ -80,34 +75,18 @@ func LookPath(file string) (string, error) {
        }
 
        if strings.ContainsAny(file, `:\/`) {
-               f, err := findExecutable(file, exts)
-               if err == nil {
+               if f, err := findExecutable(file, exts); err == nil {
                        return f, nil
+               } else {
+                       return "", &Error{file, err}
                }
-               return "", &Error{file, err}
        }
-
-       // On Windows, creating the NoDefaultCurrentDirectoryInExePath
-       // environment variable (with any value or no value!) signals that
-       // path lookups should skip the current directory.
-       // In theory we are supposed to call NeedCurrentDirectoryForExePathW
-       // "as the registry location of this environment variable can change"
-       // but that seems exceedingly unlikely: it would break all users who
-       // have configured their environment this way!
-       // https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-needcurrentdirectoryforexepathw
-       // See also go.dev/issue/43947.
-       if _, found := syscall.Getenv("NoDefaultCurrentDirectoryInExePath"); !found {
-               if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
-                       return f, &Error{file, ErrDot}
-               }
+       if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
+               return f, nil
        }
-
        path := os.Getenv("path")
        for _, dir := range filepath.SplitList(path) {
                if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
-                       if !filepath.IsAbs(f) {
-                               return f, &Error{file, ErrDot}
-                       }
                        return f, nil
                }
        }
index 1f609fffd0ddf8964b82f5445b2c9945931193d9..34abe09d049467795a96058296bdb3581678d52b 100644 (file)
@@ -8,7 +8,6 @@
 package exec_test
 
 import (
-       "errors"
        "fmt"
        "internal/testenv"
        "io"
@@ -37,9 +36,6 @@ func cmdLookPath(args ...string) {
 func cmdExec(args ...string) {
        cmd := exec.Command(args[1])
        cmd.Dir = args[0]
-       if errors.Is(cmd.Err, exec.ErrDot) {
-               cmd.Err = nil
-       }
        output, err := cmd.CombinedOutput()
        if err != nil {
                fmt.Fprintf(os.Stderr, "Child: %s %s", err, string(output))
@@ -333,7 +329,7 @@ var lookPathTests = []lookPathTest{
        },
 }
 
-func TestLookPathWindows(t *testing.T) {
+func TestLookPath(t *testing.T) {
        tmp := t.TempDir()
        printpathExe := buildPrintPathExe(t, tmp)