]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/modfetch/fetch.go
cmd/go: fix go.mod file name printed in error messages for replacements
[gostls13.git] / src / cmd / go / internal / modfetch / fetch.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package modfetch
6
7 import (
8         "archive/zip"
9         "bytes"
10         "context"
11         "crypto/sha256"
12         "encoding/base64"
13         "errors"
14         "fmt"
15         "io"
16         "io/fs"
17         "os"
18         "path/filepath"
19         "sort"
20         "strings"
21         "sync"
22
23         "cmd/go/internal/base"
24         "cmd/go/internal/cfg"
25         "cmd/go/internal/fsys"
26         "cmd/go/internal/lockedfile"
27         "cmd/go/internal/par"
28         "cmd/go/internal/robustio"
29         "cmd/go/internal/trace"
30
31         "golang.org/x/mod/module"
32         "golang.org/x/mod/sumdb/dirhash"
33         modzip "golang.org/x/mod/zip"
34 )
35
36 var downloadCache par.Cache
37
38 // Download downloads the specific module version to the
39 // local download cache and returns the name of the directory
40 // corresponding to the root of the module's file tree.
41 func Download(ctx context.Context, mod module.Version) (dir string, err error) {
42         if err := checkCacheDir(); err != nil {
43                 base.Fatalf("go: %v", err)
44         }
45
46         // The par.Cache here avoids duplicate work.
47         type cached struct {
48                 dir string
49                 err error
50         }
51         c := downloadCache.Do(mod, func() interface{} {
52                 dir, err := download(ctx, mod)
53                 if err != nil {
54                         return cached{"", err}
55                 }
56                 checkMod(mod)
57                 return cached{dir, nil}
58         }).(cached)
59         return c.dir, c.err
60 }
61
62 func download(ctx context.Context, mod module.Version) (dir string, err error) {
63         ctx, span := trace.StartSpan(ctx, "modfetch.download "+mod.String())
64         defer span.Done()
65
66         dir, err = DownloadDir(mod)
67         if err == nil {
68                 // The directory has already been completely extracted (no .partial file exists).
69                 return dir, nil
70         } else if dir == "" || !errors.Is(err, fs.ErrNotExist) {
71                 return "", err
72         }
73
74         // To avoid cluttering the cache with extraneous files,
75         // DownloadZip uses the same lockfile as Download.
76         // Invoke DownloadZip before locking the file.
77         zipfile, err := DownloadZip(ctx, mod)
78         if err != nil {
79                 return "", err
80         }
81
82         unlock, err := lockVersion(mod)
83         if err != nil {
84                 return "", err
85         }
86         defer unlock()
87
88         ctx, span = trace.StartSpan(ctx, "unzip "+zipfile)
89         defer span.Done()
90
91         // Check whether the directory was populated while we were waiting on the lock.
92         _, dirErr := DownloadDir(mod)
93         if dirErr == nil {
94                 return dir, nil
95         }
96         _, dirExists := dirErr.(*DownloadDirPartialError)
97
98         // Clean up any remaining temporary directories created by old versions
99         // (before 1.16), as well as partially extracted directories (indicated by
100         // DownloadDirPartialError, usually because of a .partial file). This is only
101         // safe to do because the lock file ensures that their writers are no longer
102         // active.
103         parentDir := filepath.Dir(dir)
104         tmpPrefix := filepath.Base(dir) + ".tmp-"
105         if old, err := filepath.Glob(filepath.Join(parentDir, tmpPrefix+"*")); err == nil {
106                 for _, path := range old {
107                         RemoveAll(path) // best effort
108                 }
109         }
110         if dirExists {
111                 if err := RemoveAll(dir); err != nil {
112                         return "", err
113                 }
114         }
115
116         partialPath, err := CachePath(mod, "partial")
117         if err != nil {
118                 return "", err
119         }
120
121         // Extract the module zip directory at its final location.
122         //
123         // To prevent other processes from reading the directory if we crash,
124         // create a .partial file before extracting the directory, and delete
125         // the .partial file afterward (all while holding the lock).
126         //
127         // Before Go 1.16, we extracted to a temporary directory with a random name
128         // then renamed it into place with os.Rename. On Windows, this failed with
129         // ERROR_ACCESS_DENIED when another process (usually an anti-virus scanner)
130         // opened files in the temporary directory.
131         //
132         // Go 1.14.2 and higher respect .partial files. Older versions may use
133         // partially extracted directories. 'go mod verify' can detect this,
134         // and 'go clean -modcache' can fix it.
135         if err := os.MkdirAll(parentDir, 0777); err != nil {
136                 return "", err
137         }
138         if err := os.WriteFile(partialPath, nil, 0666); err != nil {
139                 return "", err
140         }
141         if err := modzip.Unzip(dir, mod, zipfile); err != nil {
142                 fmt.Fprintf(os.Stderr, "-> %s\n", err)
143                 if rmErr := RemoveAll(dir); rmErr == nil {
144                         os.Remove(partialPath)
145                 }
146                 return "", err
147         }
148         if err := os.Remove(partialPath); err != nil {
149                 return "", err
150         }
151
152         if !cfg.ModCacheRW {
153                 makeDirsReadOnly(dir)
154         }
155         return dir, nil
156 }
157
158 var downloadZipCache par.Cache
159
160 // DownloadZip downloads the specific module version to the
161 // local zip cache and returns the name of the zip file.
162 func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err error) {
163         // The par.Cache here avoids duplicate work.
164         type cached struct {
165                 zipfile string
166                 err     error
167         }
168         c := downloadZipCache.Do(mod, func() interface{} {
169                 zipfile, err := CachePath(mod, "zip")
170                 if err != nil {
171                         return cached{"", err}
172                 }
173                 ziphashfile := zipfile + "hash"
174
175                 // Return without locking if the zip and ziphash files exist.
176                 if _, err := os.Stat(zipfile); err == nil {
177                         if _, err := os.Stat(ziphashfile); err == nil {
178                                 return cached{zipfile, nil}
179                         }
180                 }
181
182                 // The zip or ziphash file does not exist. Acquire the lock and create them.
183                 if cfg.CmdName != "mod download" {
184                         fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version)
185                 }
186                 unlock, err := lockVersion(mod)
187                 if err != nil {
188                         return cached{"", err}
189                 }
190                 defer unlock()
191
192                 if err := downloadZip(ctx, mod, zipfile); err != nil {
193                         return cached{"", err}
194                 }
195                 return cached{zipfile, nil}
196         }).(cached)
197         return c.zipfile, c.err
198 }
199
200 func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err error) {
201         ctx, span := trace.StartSpan(ctx, "modfetch.downloadZip "+zipfile)
202         defer span.Done()
203
204         // Double-check that the zipfile was not created while we were waiting for
205         // the lock in DownloadZip.
206         ziphashfile := zipfile + "hash"
207         var zipExists, ziphashExists bool
208         if _, err := os.Stat(zipfile); err == nil {
209                 zipExists = true
210         }
211         if _, err := os.Stat(ziphashfile); err == nil {
212                 ziphashExists = true
213         }
214         if zipExists && ziphashExists {
215                 return nil
216         }
217
218         // Create parent directories.
219         if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
220                 return err
221         }
222
223         // Clean up any remaining tempfiles from previous runs.
224         // This is only safe to do because the lock file ensures that their
225         // writers are no longer active.
226         tmpPattern := filepath.Base(zipfile) + "*.tmp"
227         if old, err := filepath.Glob(filepath.Join(filepath.Dir(zipfile), tmpPattern)); err == nil {
228                 for _, path := range old {
229                         os.Remove(path) // best effort
230                 }
231         }
232
233         // If the zip file exists, the ziphash file must have been deleted
234         // or lost after a file system crash. Re-hash the zip without downloading.
235         if zipExists {
236                 return hashZip(mod, zipfile, ziphashfile)
237         }
238
239         // From here to the os.Rename call below is functionally almost equivalent to
240         // renameio.WriteToFile, with one key difference: we want to validate the
241         // contents of the file (by hashing it) before we commit it. Because the file
242         // is zip-compressed, we need an actual file â€” or at least an io.ReaderAt â€” to
243         // validate it: we can't just tee the stream as we write it.
244         f, err := os.CreateTemp(filepath.Dir(zipfile), tmpPattern)
245         if err != nil {
246                 return err
247         }
248         defer func() {
249                 if err != nil {
250                         f.Close()
251                         os.Remove(f.Name())
252                 }
253         }()
254
255         var unrecoverableErr error
256         err = TryProxies(func(proxy string) error {
257                 if unrecoverableErr != nil {
258                         return unrecoverableErr
259                 }
260                 repo := Lookup(proxy, mod.Path)
261                 err := repo.Zip(f, mod.Version)
262                 if err != nil {
263                         // Zip may have partially written to f before failing.
264                         // (Perhaps the server crashed while sending the file?)
265                         // Since we allow fallback on error in some cases, we need to fix up the
266                         // file to be empty again for the next attempt.
267                         if _, err := f.Seek(0, io.SeekStart); err != nil {
268                                 unrecoverableErr = err
269                                 return err
270                         }
271                         if err := f.Truncate(0); err != nil {
272                                 unrecoverableErr = err
273                                 return err
274                         }
275                 }
276                 return err
277         })
278         if err != nil {
279                 return err
280         }
281
282         // Double-check that the paths within the zip file are well-formed.
283         //
284         // TODO(bcmills): There is a similar check within the Unzip function. Can we eliminate one?
285         fi, err := f.Stat()
286         if err != nil {
287                 return err
288         }
289         z, err := zip.NewReader(f, fi.Size())
290         if err != nil {
291                 return err
292         }
293         prefix := mod.Path + "@" + mod.Version + "/"
294         for _, f := range z.File {
295                 if !strings.HasPrefix(f.Name, prefix) {
296                         return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
297                 }
298         }
299
300         if err := f.Close(); err != nil {
301                 return err
302         }
303
304         // Hash the zip file and check the sum before renaming to the final location.
305         if err := hashZip(mod, f.Name(), ziphashfile); err != nil {
306                 return err
307         }
308         if err := os.Rename(f.Name(), zipfile); err != nil {
309                 return err
310         }
311
312         // TODO(bcmills): Should we make the .zip and .ziphash files read-only to discourage tampering?
313
314         return nil
315 }
316
317 // hashZip reads the zip file opened in f, then writes the hash to ziphashfile,
318 // overwriting that file if it exists.
319 //
320 // If the hash does not match go.sum (or the sumdb if enabled), hashZip returns
321 // an error and does not write ziphashfile.
322 func hashZip(mod module.Version, zipfile, ziphashfile string) error {
323         hash, err := dirhash.HashZip(zipfile, dirhash.DefaultHash)
324         if err != nil {
325                 return err
326         }
327         if err := checkModSum(mod, hash); err != nil {
328                 return err
329         }
330         hf, err := lockedfile.Create(ziphashfile)
331         if err != nil {
332                 return err
333         }
334         if err := hf.Truncate(int64(len(hash))); err != nil {
335                 return err
336         }
337         if _, err := hf.WriteAt([]byte(hash), 0); err != nil {
338                 return err
339         }
340         if err := hf.Close(); err != nil {
341                 return err
342         }
343
344         return nil
345 }
346
347 // makeDirsReadOnly makes a best-effort attempt to remove write permissions for dir
348 // and its transitive contents.
349 func makeDirsReadOnly(dir string) {
350         type pathMode struct {
351                 path string
352                 mode fs.FileMode
353         }
354         var dirs []pathMode // in lexical order
355         filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
356                 if err == nil && d.IsDir() {
357                         info, err := d.Info()
358                         if err == nil && info.Mode()&0222 != 0 {
359                                 dirs = append(dirs, pathMode{path, info.Mode()})
360                         }
361                 }
362                 return nil
363         })
364
365         // Run over list backward to chmod children before parents.
366         for i := len(dirs) - 1; i >= 0; i-- {
367                 os.Chmod(dirs[i].path, dirs[i].mode&^0222)
368         }
369 }
370
371 // RemoveAll removes a directory written by Download or Unzip, first applying
372 // any permission changes needed to do so.
373 func RemoveAll(dir string) error {
374         // Module cache has 0555 directories; make them writable in order to remove content.
375         filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error {
376                 if err != nil {
377                         return nil // ignore errors walking in file system
378                 }
379                 if info.IsDir() {
380                         os.Chmod(path, 0777)
381                 }
382                 return nil
383         })
384         return robustio.RemoveAll(dir)
385 }
386
387 var GoSumFile string // path to go.sum; set by package modload
388
389 type modSum struct {
390         mod module.Version
391         sum string
392 }
393
394 var goSum struct {
395         mu        sync.Mutex
396         m         map[module.Version][]string // content of go.sum file
397         status    map[modSum]modSumStatus     // state of sums in m
398         overwrite bool                        // if true, overwrite go.sum without incorporating its contents
399         enabled   bool                        // whether to use go.sum at all
400 }
401
402 type modSumStatus struct {
403         used, dirty bool
404 }
405
406 // initGoSum initializes the go.sum data.
407 // The boolean it returns reports whether the
408 // use of go.sum is now enabled.
409 // The goSum lock must be held.
410 func initGoSum() (bool, error) {
411         if GoSumFile == "" {
412                 return false, nil
413         }
414         if goSum.m != nil {
415                 return true, nil
416         }
417
418         goSum.m = make(map[module.Version][]string)
419         goSum.status = make(map[modSum]modSumStatus)
420         var (
421                 data []byte
422                 err  error
423         )
424         if actualSumFile, ok := fsys.OverlayPath(GoSumFile); ok {
425                 // Don't lock go.sum if it's part of the overlay.
426                 // On Plan 9, locking requires chmod, and we don't want to modify any file
427                 // in the overlay. See #44700.
428                 data, err = os.ReadFile(actualSumFile)
429         } else {
430                 data, err = lockedfile.Read(GoSumFile)
431         }
432         if err != nil && !os.IsNotExist(err) {
433                 return false, err
434         }
435         goSum.enabled = true
436         readGoSum(goSum.m, GoSumFile, data)
437
438         return true, nil
439 }
440
441 // emptyGoModHash is the hash of a 1-file tree containing a 0-length go.mod.
442 // A bug caused us to write these into go.sum files for non-modules.
443 // We detect and remove them.
444 const emptyGoModHash = "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY="
445
446 // readGoSum parses data, which is the content of file,
447 // and adds it to goSum.m. The goSum lock must be held.
448 func readGoSum(dst map[module.Version][]string, file string, data []byte) error {
449         lineno := 0
450         for len(data) > 0 {
451                 var line []byte
452                 lineno++
453                 i := bytes.IndexByte(data, '\n')
454                 if i < 0 {
455                         line, data = data, nil
456                 } else {
457                         line, data = data[:i], data[i+1:]
458                 }
459                 f := strings.Fields(string(line))
460                 if len(f) == 0 {
461                         // blank line; skip it
462                         continue
463                 }
464                 if len(f) != 3 {
465                         return fmt.Errorf("malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
466                 }
467                 if f[2] == emptyGoModHash {
468                         // Old bug; drop it.
469                         continue
470                 }
471                 mod := module.Version{Path: f[0], Version: f[1]}
472                 dst[mod] = append(dst[mod], f[2])
473         }
474         return nil
475 }
476
477 // HaveSum returns true if the go.sum file contains an entry for mod.
478 // The entry's hash must be generated with a known hash algorithm.
479 // mod.Version may have a "/go.mod" suffix to distinguish sums for
480 // .mod and .zip files.
481 func HaveSum(mod module.Version) bool {
482         goSum.mu.Lock()
483         defer goSum.mu.Unlock()
484         inited, err := initGoSum()
485         if err != nil || !inited {
486                 return false
487         }
488         for _, h := range goSum.m[mod] {
489                 if !strings.HasPrefix(h, "h1:") {
490                         continue
491                 }
492                 if !goSum.status[modSum{mod, h}].dirty {
493                         return true
494                 }
495         }
496         return false
497 }
498
499 // checkMod checks the given module's checksum.
500 func checkMod(mod module.Version) {
501         // Do the file I/O before acquiring the go.sum lock.
502         ziphash, err := CachePath(mod, "ziphash")
503         if err != nil {
504                 base.Fatalf("verifying %v", module.VersionError(mod, err))
505         }
506         data, err := lockedfile.Read(ziphash)
507         if err != nil {
508                 base.Fatalf("verifying %v", module.VersionError(mod, err))
509         }
510         data = bytes.TrimSpace(data)
511         if !isValidSum(data) {
512                 // Recreate ziphash file from zip file and use that to check the mod sum.
513                 zip, err := CachePath(mod, "zip")
514                 if err != nil {
515                         base.Fatalf("verifying %v", module.VersionError(mod, err))
516                 }
517                 err = hashZip(mod, zip, ziphash)
518                 if err != nil {
519                         base.Fatalf("verifying %v", module.VersionError(mod, err))
520                 }
521                 return
522         }
523         h := string(data)
524         if !strings.HasPrefix(h, "h1:") {
525                 base.Fatalf("verifying %v", module.VersionError(mod, fmt.Errorf("unexpected ziphash: %q", h)))
526         }
527
528         if err := checkModSum(mod, h); err != nil {
529                 base.Fatalf("%s", err)
530         }
531 }
532
533 // goModSum returns the checksum for the go.mod contents.
534 func goModSum(data []byte) (string, error) {
535         return dirhash.Hash1([]string{"go.mod"}, func(string) (io.ReadCloser, error) {
536                 return io.NopCloser(bytes.NewReader(data)), nil
537         })
538 }
539
540 // checkGoMod checks the given module's go.mod checksum;
541 // data is the go.mod content.
542 func checkGoMod(path, version string, data []byte) error {
543         h, err := goModSum(data)
544         if err != nil {
545                 return &module.ModuleError{Path: path, Version: version, Err: fmt.Errorf("verifying go.mod: %v", err)}
546         }
547
548         return checkModSum(module.Version{Path: path, Version: version + "/go.mod"}, h)
549 }
550
551 // checkModSum checks that the recorded checksum for mod is h.
552 //
553 // mod.Version may have the additional suffix "/go.mod" to request the checksum
554 // for the module's go.mod file only.
555 func checkModSum(mod module.Version, h string) error {
556         // We lock goSum when manipulating it,
557         // but we arrange to release the lock when calling checkSumDB,
558         // so that parallel calls to checkModHash can execute parallel calls
559         // to checkSumDB.
560
561         // Check whether mod+h is listed in go.sum already. If so, we're done.
562         goSum.mu.Lock()
563         inited, err := initGoSum()
564         if err != nil {
565                 goSum.mu.Unlock()
566                 return err
567         }
568         done := inited && haveModSumLocked(mod, h)
569         if inited {
570                 st := goSum.status[modSum{mod, h}]
571                 st.used = true
572                 goSum.status[modSum{mod, h}] = st
573         }
574         goSum.mu.Unlock()
575
576         if done {
577                 return nil
578         }
579
580         // Not listed, so we want to add them.
581         // Consult checksum database if appropriate.
582         if useSumDB(mod) {
583                 // Calls base.Fatalf if mismatch detected.
584                 if err := checkSumDB(mod, h); err != nil {
585                         return err
586                 }
587         }
588
589         // Add mod+h to go.sum, if it hasn't appeared already.
590         if inited {
591                 goSum.mu.Lock()
592                 addModSumLocked(mod, h)
593                 st := goSum.status[modSum{mod, h}]
594                 st.dirty = true
595                 goSum.status[modSum{mod, h}] = st
596                 goSum.mu.Unlock()
597         }
598         return nil
599 }
600
601 // haveModSumLocked reports whether the pair mod,h is already listed in go.sum.
602 // If it finds a conflicting pair instead, it calls base.Fatalf.
603 // goSum.mu must be locked.
604 func haveModSumLocked(mod module.Version, h string) bool {
605         for _, vh := range goSum.m[mod] {
606                 if h == vh {
607                         return true
608                 }
609                 if strings.HasPrefix(vh, "h1:") {
610                         base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum:     %v"+goSumMismatch, mod.Path, mod.Version, h, vh)
611                 }
612         }
613         return false
614 }
615
616 // addModSumLocked adds the pair mod,h to go.sum.
617 // goSum.mu must be locked.
618 func addModSumLocked(mod module.Version, h string) {
619         if haveModSumLocked(mod, h) {
620                 return
621         }
622         if len(goSum.m[mod]) > 0 {
623                 fmt.Fprintf(os.Stderr, "warning: verifying %s@%s: unknown hashes in go.sum: %v; adding %v"+hashVersionMismatch, mod.Path, mod.Version, strings.Join(goSum.m[mod], ", "), h)
624         }
625         goSum.m[mod] = append(goSum.m[mod], h)
626 }
627
628 // checkSumDB checks the mod, h pair against the Go checksum database.
629 // It calls base.Fatalf if the hash is to be rejected.
630 func checkSumDB(mod module.Version, h string) error {
631         modWithoutSuffix := mod
632         noun := "module"
633         if strings.HasSuffix(mod.Version, "/go.mod") {
634                 noun = "go.mod"
635                 modWithoutSuffix.Version = strings.TrimSuffix(mod.Version, "/go.mod")
636         }
637
638         db, lines, err := lookupSumDB(mod)
639         if err != nil {
640                 return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: %v", noun, err))
641         }
642
643         have := mod.Path + " " + mod.Version + " " + h
644         prefix := mod.Path + " " + mod.Version + " h1:"
645         for _, line := range lines {
646                 if line == have {
647                         return nil
648                 }
649                 if strings.HasPrefix(line, prefix) {
650                         return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+sumdbMismatch, noun, h, db, line[len(prefix)-len("h1:"):]))
651                 }
652         }
653         return nil
654 }
655
656 // Sum returns the checksum for the downloaded copy of the given module,
657 // if present in the download cache.
658 func Sum(mod module.Version) string {
659         if cfg.GOMODCACHE == "" {
660                 // Do not use current directory.
661                 return ""
662         }
663
664         ziphash, err := CachePath(mod, "ziphash")
665         if err != nil {
666                 return ""
667         }
668         data, err := lockedfile.Read(ziphash)
669         if err != nil {
670                 return ""
671         }
672         data = bytes.TrimSpace(data)
673         if !isValidSum(data) {
674                 return ""
675         }
676         return string(data)
677 }
678
679 // isValidSum returns true if data is the valid contents of a zip hash file.
680 // Certain critical files are written to disk by first truncating
681 // then writing the actual bytes, so that if the write fails
682 // the corrupt file should contain at least one of the null
683 // bytes written by the truncate operation.
684 func isValidSum(data []byte) bool {
685         if bytes.IndexByte(data, '\000') >= 0 {
686                 return false
687         }
688
689         if len(data) != len("h1:")+base64.StdEncoding.EncodedLen(sha256.Size) {
690                 return false
691         }
692
693         return true
694 }
695
696 // WriteGoSum writes the go.sum file if it needs to be updated.
697 //
698 // keep is used to check whether a newly added sum should be saved in go.sum.
699 // It should have entries for both module content sums and go.mod sums
700 // (version ends with "/go.mod"). Existing sums will be preserved unless they
701 // have been marked for deletion with TrimGoSum.
702 func WriteGoSum(keep map[module.Version]bool) {
703         goSum.mu.Lock()
704         defer goSum.mu.Unlock()
705
706         // If we haven't read the go.sum file yet, don't bother writing it.
707         if !goSum.enabled {
708                 return
709         }
710
711         // Check whether we need to add sums for which keep[m] is true or remove
712         // unused sums marked with TrimGoSum. If there are no changes to make,
713         // just return without opening go.sum.
714         dirty := false
715 Outer:
716         for m, hs := range goSum.m {
717                 for _, h := range hs {
718                         st := goSum.status[modSum{m, h}]
719                         if st.dirty && (!st.used || keep[m]) {
720                                 dirty = true
721                                 break Outer
722                         }
723                 }
724         }
725         if !dirty {
726                 return
727         }
728         if cfg.BuildMod == "readonly" {
729                 base.Fatalf("go: updates to go.sum needed, disabled by -mod=readonly")
730         }
731         if _, ok := fsys.OverlayPath(GoSumFile); ok {
732                 base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
733         }
734
735         // Make a best-effort attempt to acquire the side lock, only to exclude
736         // previous versions of the 'go' command from making simultaneous edits.
737         if unlock, err := SideLock(); err == nil {
738                 defer unlock()
739         }
740
741         err := lockedfile.Transform(GoSumFile, func(data []byte) ([]byte, error) {
742                 if !goSum.overwrite {
743                         // Incorporate any sums added by other processes in the meantime.
744                         // Add only the sums that we actually checked: the user may have edited or
745                         // truncated the file to remove erroneous hashes, and we shouldn't restore
746                         // them without good reason.
747                         goSum.m = make(map[module.Version][]string, len(goSum.m))
748                         readGoSum(goSum.m, GoSumFile, data)
749                         for ms, st := range goSum.status {
750                                 if st.used {
751                                         addModSumLocked(ms.mod, ms.sum)
752                                 }
753                         }
754                 }
755
756                 var mods []module.Version
757                 for m := range goSum.m {
758                         mods = append(mods, m)
759                 }
760                 module.Sort(mods)
761
762                 var buf bytes.Buffer
763                 for _, m := range mods {
764                         list := goSum.m[m]
765                         sort.Strings(list)
766                         for _, h := range list {
767                                 st := goSum.status[modSum{m, h}]
768                                 if !st.dirty || (st.used && keep[m]) {
769                                         fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
770                                 }
771                         }
772                 }
773                 return buf.Bytes(), nil
774         })
775
776         if err != nil {
777                 base.Fatalf("go: updating go.sum: %v", err)
778         }
779
780         goSum.status = make(map[modSum]modSumStatus)
781         goSum.overwrite = false
782 }
783
784 // TrimGoSum trims go.sum to contain only the modules needed for reproducible
785 // builds.
786 //
787 // keep is used to check whether a sum should be retained in go.mod. It should
788 // have entries for both module content sums and go.mod sums (version ends
789 // with "/go.mod").
790 func TrimGoSum(keep map[module.Version]bool) {
791         goSum.mu.Lock()
792         defer goSum.mu.Unlock()
793         inited, err := initGoSum()
794         if err != nil {
795                 base.Fatalf("%s", err)
796         }
797         if !inited {
798                 return
799         }
800
801         for m, hs := range goSum.m {
802                 if !keep[m] {
803                         for _, h := range hs {
804                                 goSum.status[modSum{m, h}] = modSumStatus{used: false, dirty: true}
805                         }
806                         goSum.overwrite = true
807                 }
808         }
809 }
810
811 const goSumMismatch = `
812
813 SECURITY ERROR
814 This download does NOT match an earlier download recorded in go.sum.
815 The bits may have been replaced on the origin server, or an attacker may
816 have intercepted the download attempt.
817
818 For more information, see 'go help module-auth'.
819 `
820
821 const sumdbMismatch = `
822
823 SECURITY ERROR
824 This download does NOT match the one reported by the checksum server.
825 The bits may have been replaced on the origin server, or an attacker may
826 have intercepted the download attempt.
827
828 For more information, see 'go help module-auth'.
829 `
830
831 const hashVersionMismatch = `
832
833 SECURITY WARNING
834 This download is listed in go.sum, but using an unknown hash algorithm.
835 The download cannot be verified.
836
837 For more information, see 'go help module-auth'.
838
839 `
840
841 var HelpModuleAuth = &base.Command{
842         UsageLine: "module-auth",
843         Short:     "module authentication using go.sum",
844         Long: `
845 When the go command downloads a module zip file or go.mod file into the
846 module cache, it computes a cryptographic hash and compares it with a known
847 value to verify the file hasn't changed since it was first downloaded. Known
848 hashes are stored in a file in the module root directory named go.sum. Hashes
849 may also be downloaded from the checksum database depending on the values of
850 GOSUMDB, GOPRIVATE, and GONOSUMDB.
851
852 For details, see https://golang.org/ref/mod#authenticating.
853 `,
854 }
855
856 var HelpPrivate = &base.Command{
857         UsageLine: "private",
858         Short:     "configuration for downloading non-public code",
859         Long: `
860 The go command defaults to downloading modules from the public Go module
861 mirror at proxy.golang.org. It also defaults to validating downloaded modules,
862 regardless of source, against the public Go checksum database at sum.golang.org.
863 These defaults work well for publicly available source code.
864
865 The GOPRIVATE environment variable controls which modules the go command
866 considers to be private (not available publicly) and should therefore not use
867 the proxy or checksum database. The variable is a comma-separated list of
868 glob patterns (in the syntax of Go's path.Match) of module path prefixes.
869 For example,
870
871         GOPRIVATE=*.corp.example.com,rsc.io/private
872
873 causes the go command to treat as private any module with a path prefix
874 matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
875 and rsc.io/private/quux.
876
877 For fine-grained control over module download and validation, the GONOPROXY
878 and GONOSUMDB environment variables accept the same kind of glob list
879 and override GOPRIVATE for the specific decision of whether to use the proxy
880 and checksum database, respectively.
881
882 For example, if a company ran a module proxy serving private modules,
883 users would configure go using:
884
885         GOPRIVATE=*.corp.example.com
886         GOPROXY=proxy.example.com
887         GONOPROXY=none
888
889 The GOPRIVATE variable is also used to define the "public" and "private"
890 patterns for the GOVCS variable; see 'go help vcs'. For that usage,
891 GOPRIVATE applies even in GOPATH mode. In that case, it matches import paths
892 instead of module paths.
893
894 The 'go env -w' command (see 'go help env') can be used to set these variables
895 for future go command invocations.
896
897 For more details, see https://golang.org/ref/mod#private-modules.
898 `,
899 }