From 3f46587fe05f745a0bf91b74c2834f1317acba24 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Tue, 18 Feb 2025 15:31:14 -0500 Subject: [PATCH] cmd/go: explicitly reject overlays affecting GOMODCACHE The go command assumes that GOMODCACHE is immutable. As an example of one place the assumption is made, the modindex won't stat the files in GOMODCACHE when getting the cache key for the index entry and just uses the path of the module in the modcache (basically the module's name and version). Explicitly reject overlays affecting GOMODCACHE to avoid surprising and incorrect behavior. For #71783 For #71075 Change-Id: I21dd5d39d71037de473b09ac8482a1867864e11f Reviewed-on: https://go-review.googlesource.com/c/go/+/650475 LUCI-TryBot-Result: Go LUCI Reviewed-by: Alan Donovan Reviewed-by: Robert Findley --- src/cmd/go/alldocs.go | 7 ++++--- src/cmd/go/internal/fsys/fsys.go | 26 ++++++++++++++++++++++++++ src/cmd/go/internal/work/build.go | 7 ++++--- src/cmd/go/internal/work/init.go | 3 +++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 6f0cb1b698..7805f86357 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -211,15 +211,16 @@ // -modfile flag by trimming the ".mod" extension and appending ".sum". // -overlay file // read a JSON config file that provides an overlay for build operations. -// The file is a JSON struct with a single field, named 'Replace', that +// The file is a JSON object with a single field, named 'Replace', that // maps each disk file path (a string) to its backing file path, so that // a build will run as if the disk file path exists with the contents // given by the backing file paths, or as if the disk file path does not // exist if its backing file path is empty. Support for the -overlay flag // has some limitations: importantly, cgo files included from outside the // include path must be in the same directory as the Go package they are -// included from, and overlays will not appear when binaries and tests are -// run through go run and go test respectively. +// included from, overlays will not appear when binaries and tests are +// run through go run and go test respectively, and files beneath +// GOMODCACHE may not be replaced. // -pgo file // specify the file path of a profile for profile-guided optimization (PGO). // When the special name "auto" is specified, for each main package in the diff --git a/src/cmd/go/internal/fsys/fsys.go b/src/cmd/go/internal/fsys/fsys.go index 9387e165d6..0e0821a35f 100644 --- a/src/cmd/go/internal/fsys/fsys.go +++ b/src/cmd/go/internal/fsys/fsys.go @@ -520,6 +520,32 @@ func Replaced(name string) bool { return info.deleted || info.replaced && !info.dir } +// DirContainsReplacement reports whether the named directory is affected by a replacement, +// either because a parent directory has been replaced, it has been replaced, or a file or +// directory under it has been replaced. +// It is meant to be used to detect cases where GOMODCACHE has been replaced. That replacement +// is not supported (GOMODCACHE is meant to be immutable) and the caller will use the +// information to return an error. +func DirContainsReplacement(name string) (string, bool) { + apath := abs(name) + + // Check the overlay using similar logic to what stat uses. + i, ok := slices.BinarySearchFunc(overlay, apath, searchcmp) + if ok { + // The named directory itself has been replaced. + return overlay[i].from, true + } + if i < len(overlay) && str.HasFilePathPrefix(overlay[i].from, apath) { + // A file or directory contained in the named directory has been replaced. + return overlay[i].from, true + } + if i > 0 && str.HasFilePathPrefix(apath, overlay[i-1].from) { + // A parent of the named directory has been replaced. + return overlay[i-1].from, true + } + return "", false +} + // Open opens the named file in the virtual file system. // It must be an ordinary file, not a directory. func Open(name string) (*os.File, error) { diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index 873feb8a26..4f81f1390c 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -167,15 +167,16 @@ and test commands: -modfile flag by trimming the ".mod" extension and appending ".sum". -overlay file read a JSON config file that provides an overlay for build operations. - The file is a JSON struct with a single field, named 'Replace', that + The file is a JSON object with a single field, named 'Replace', that maps each disk file path (a string) to its backing file path, so that a build will run as if the disk file path exists with the contents given by the backing file paths, or as if the disk file path does not exist if its backing file path is empty. Support for the -overlay flag has some limitations: importantly, cgo files included from outside the include path must be in the same directory as the Go package they are - included from, and overlays will not appear when binaries and tests are - run through go run and go test respectively. + included from, overlays will not appear when binaries and tests are + run through go run and go test respectively, and files beneath + GOMODCACHE may not be replaced. -pgo file specify the file path of a profile for profile-guided optimization (PGO). When the special name "auto" is specified, for each main package in the diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index 831c64bada..adee7c0274 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -39,6 +39,9 @@ func BuildInit() { if err := fsys.Init(); err != nil { base.Fatal(err) } + if from, replaced := fsys.DirContainsReplacement(cfg.GOMODCACHE); replaced { + base.Fatalf("go: overlay contains a replacement for %s. Files beneath GOMODCACHE (%s) must not be replaced.", from, cfg.GOMODCACHE) + } // Make sure -pkgdir is absolute, because we run commands // in different directories. -- 2.51.0