summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/modcmd/verify.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/modcmd/verify.go')
-rw-r--r--src/cmd/go/internal/modcmd/verify.go128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
new file mode 100644
index 0000000..8321429
--- /dev/null
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -0,0 +1,128 @@
+// Copyright 2018 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 modcmd
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io/fs"
+ "os"
+ "runtime"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/modfetch"
+ "cmd/go/internal/modload"
+
+ "golang.org/x/mod/module"
+ "golang.org/x/mod/sumdb/dirhash"
+)
+
+var cmdVerify = &base.Command{
+ UsageLine: "go mod verify",
+ Short: "verify dependencies have expected content",
+ Long: `
+Verify checks that the dependencies of the current module,
+which are stored in a local downloaded source cache, have not been
+modified since being downloaded. If all the modules are unmodified,
+verify prints "all modules verified." Otherwise it reports which
+modules have been changed and causes 'go mod' to exit with a
+non-zero status.
+
+See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
+ `,
+ Run: runVerify,
+}
+
+func init() {
+ base.AddModCommonFlags(&cmdVerify.Flag)
+}
+
+func runVerify(ctx context.Context, cmd *base.Command, args []string) {
+ if len(args) != 0 {
+ // NOTE(rsc): Could take a module pattern.
+ base.Fatalf("go mod verify: verify takes no arguments")
+ }
+ modload.ForceUseModules = true
+ modload.RootMode = modload.NeedRoot
+
+ // Only verify up to GOMAXPROCS zips at once.
+ type token struct{}
+ sem := make(chan token, runtime.GOMAXPROCS(0))
+
+ // Use a slice of result channels, so that the output is deterministic.
+ mods := modload.LoadAllModules(ctx)[1:]
+ errsChans := make([]<-chan []error, len(mods))
+
+ for i, mod := range mods {
+ sem <- token{}
+ errsc := make(chan []error, 1)
+ errsChans[i] = errsc
+ mod := mod // use a copy to avoid data races
+ go func() {
+ errsc <- verifyMod(mod)
+ <-sem
+ }()
+ }
+
+ ok := true
+ for _, errsc := range errsChans {
+ errs := <-errsc
+ for _, err := range errs {
+ base.Errorf("%s", err)
+ ok = false
+ }
+ }
+ if ok {
+ fmt.Printf("all modules verified\n")
+ }
+}
+
+func verifyMod(mod module.Version) []error {
+ var errs []error
+ zip, zipErr := modfetch.CachePath(mod, "zip")
+ if zipErr == nil {
+ _, zipErr = os.Stat(zip)
+ }
+ dir, dirErr := modfetch.DownloadDir(mod)
+ data, err := os.ReadFile(zip + "hash")
+ if err != nil {
+ if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) &&
+ dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
+ // Nothing downloaded yet. Nothing to verify.
+ return nil
+ }
+ errs = append(errs, fmt.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err))
+ return errs
+ }
+ h := string(bytes.TrimSpace(data))
+
+ if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) {
+ // ok
+ } else {
+ hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
+ return errs
+ } else if hZ != h {
+ errs = append(errs, fmt.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip))
+ }
+ }
+ if dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
+ // ok
+ } else {
+ hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
+ if err != nil {
+
+ errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
+ return errs
+ }
+ if hD != h {
+ errs = append(errs, fmt.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir))
+ }
+ }
+ return errs
+}