diff options
| author | Sean Liao <sean@liao.dev> | 2025-10-19 20:00:55 +0100 |
|---|---|---|
| committer | Sean Liao <sean@liao.dev> | 2025-10-22 13:35:02 -0700 |
| commit | 7c9fa4d5e9b65be396d7794f645b1f409a9bc39f (patch) | |
| tree | 1012bdbb5dc26ba65e995787853fdfd92603ebc8 /src | |
| parent | 557b4d6e0f9de96ce9718f89773f14e6e1b14760 (diff) | |
| download | go-7c9fa4d5e9b65be396d7794f645b1f409a9bc39f.tar.xz | |
cmd/go: check if build output should overwrite files with renames
CopyFile has a check to ensure that only object files are overwritten.
Extend this to moveOrCopyFile, so the check also happens when the source
and destination file are on the same filesystem (when renames are a
valid way of moving files).
Fixes #75970
Change-Id: Ie667301f1c9c00b114cfd91cdf8053ac20fd817b
Reviewed-on: https://go-review.googlesource.com/c/go/+/712960
Reviewed-by: Laurent Demailly <ldemailly@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Matloob <matloob@google.com>
Reviewed-by: Ian Alexander <jitsu@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
Diffstat (limited to 'src')
| -rw-r--r-- | src/cmd/go/internal/work/shell.go | 33 | ||||
| -rw-r--r-- | src/cmd/go/testdata/script/build_output_overwrite.txt | 20 |
2 files changed, 43 insertions, 10 deletions
diff --git a/src/cmd/go/internal/work/shell.go b/src/cmd/go/internal/work/shell.go index 284ed26f22..ceff84d81f 100644 --- a/src/cmd/go/internal/work/shell.go +++ b/src/cmd/go/internal/work/shell.go @@ -123,6 +123,11 @@ func (sh *Shell) moveOrCopyFile(dst, src string, perm fs.FileMode, force bool) e return nil } + err := checkDstOverwrite(dst, force) + if err != nil { + return err + } + // If we can update the mode and rename to the dst, do it. // Otherwise fall back to standard copy. @@ -193,16 +198,9 @@ func (sh *Shell) CopyFile(dst, src string, perm fs.FileMode, force bool) error { } defer sf.Close() - // Be careful about removing/overwriting dst. - // Do not remove/overwrite if dst exists and is a directory - // or a non-empty non-object file. - if fi, err := os.Stat(dst); err == nil { - if fi.IsDir() { - return fmt.Errorf("build output %q already exists and is a directory", dst) - } - if !force && fi.Mode().IsRegular() && fi.Size() != 0 && !isObject(dst) { - return fmt.Errorf("build output %q already exists and is not an object file", dst) - } + err = checkDstOverwrite(dst, force) + if err != nil { + return err } // On Windows, remove lingering ~ file from last attempt. @@ -247,6 +245,21 @@ func mayberemovefile(s string) { os.Remove(s) } +// Be careful about removing/overwriting dst. +// Do not remove/overwrite if dst exists and is a directory +// or a non-empty non-object file. +func checkDstOverwrite(dst string, force bool) error { + if fi, err := os.Stat(dst); err == nil { + if fi.IsDir() { + return fmt.Errorf("build output %q already exists and is a directory", dst) + } + if !force && fi.Mode().IsRegular() && fi.Size() != 0 && !isObject(dst) { + return fmt.Errorf("build output %q already exists and is not an object file", dst) + } + } + return nil +} + // writeFile writes the text to file. func (sh *Shell) writeFile(file string, text []byte) error { if cfg.BuildN || cfg.BuildX { diff --git a/src/cmd/go/testdata/script/build_output_overwrite.txt b/src/cmd/go/testdata/script/build_output_overwrite.txt new file mode 100644 index 0000000000..c7b967ccec --- /dev/null +++ b/src/cmd/go/testdata/script/build_output_overwrite.txt @@ -0,0 +1,20 @@ +# windows executables have the .exe extension and won't overwrite source files +[GOOS:windows] skip + +mkdir out +env GOTMPDIR=$PWD/out + +grep 'this should still exist' foo.go + +! go build +stderr 'already exists and is not an object file' + +grep 'this should still exist' foo.go + +-- go.mod -- +module foo.go + +-- foo.go -- +package main // this should still exist + +func main() {} |
