]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/modfetch/fetch.go
[dev.cmdgo] all: merge master (9eee0ed) into dev.cmdgo
[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 var ErrGoSumDirty = errors.New("updates to go.sum needed, disabled by -mod=readonly")
697
698 // WriteGoSum writes the go.sum file if it needs to be updated.
699 //
700 // keep is used to check whether a newly added sum should be saved in go.sum.
701 // It should have entries for both module content sums and go.mod sums
702 // (version ends with "/go.mod"). Existing sums will be preserved unless they
703 // have been marked for deletion with TrimGoSum.
704 func WriteGoSum(keep map[module.Version]bool, readonly bool) error {
705         goSum.mu.Lock()
706         defer goSum.mu.Unlock()
707
708         // If we haven't read the go.sum file yet, don't bother writing it.
709         if !goSum.enabled {
710                 return nil
711         }
712
713         // Check whether we need to add sums for which keep[m] is true or remove
714         // unused sums marked with TrimGoSum. If there are no changes to make,
715         // just return without opening go.sum.
716         dirty := false
717 Outer:
718         for m, hs := range goSum.m {
719                 for _, h := range hs {
720                         st := goSum.status[modSum{m, h}]
721                         if st.dirty && (!st.used || keep[m]) {
722                                 dirty = true
723                                 break Outer
724                         }
725                 }
726         }
727         if !dirty {
728                 return nil
729         }
730         if readonly {
731                 return ErrGoSumDirty
732         }
733         if _, ok := fsys.OverlayPath(GoSumFile); ok {
734                 base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
735         }
736
737         // Make a best-effort attempt to acquire the side lock, only to exclude
738         // previous versions of the 'go' command from making simultaneous edits.
739         if unlock, err := SideLock(); err == nil {
740                 defer unlock()
741         }
742
743         err := lockedfile.Transform(GoSumFile, func(data []byte) ([]byte, error) {
744                 if !goSum.overwrite {
745                         // Incorporate any sums added by other processes in the meantime.
746                         // Add only the sums that we actually checked: the user may have edited or
747                         // truncated the file to remove erroneous hashes, and we shouldn't restore
748                         // them without good reason.
749                         goSum.m = make(map[module.Version][]string, len(goSum.m))
750                         readGoSum(goSum.m, GoSumFile, data)
751                         for ms, st := range goSum.status {
752                                 if st.used {
753                                         addModSumLocked(ms.mod, ms.sum)
754                                 }
755                         }
756                 }
757
758                 var mods []module.Version
759                 for m := range goSum.m {
760                         mods = append(mods, m)
761                 }
762                 module.Sort(mods)
763
764                 var buf bytes.Buffer
765                 for _, m := range mods {
766                         list := goSum.m[m]
767                         sort.Strings(list)
768                         for _, h := range list {
769                                 st := goSum.status[modSum{m, h}]
770                                 if !st.dirty || (st.used && keep[m]) {
771                                         fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
772                                 }
773                         }
774                 }
775                 return buf.Bytes(), nil
776         })
777
778         if err != nil {
779                 return fmt.Errorf("updating go.sum: %w", err)
780         }
781
782         goSum.status = make(map[modSum]modSumStatus)
783         goSum.overwrite = false
784         return nil
785 }
786
787 // TrimGoSum trims go.sum to contain only the modules needed for reproducible
788 // builds.
789 //
790 // keep is used to check whether a sum should be retained in go.mod. It should
791 // have entries for both module content sums and go.mod sums (version ends
792 // with "/go.mod").
793 func TrimGoSum(keep map[module.Version]bool) {
794         goSum.mu.Lock()
795         defer goSum.mu.Unlock()
796         inited, err := initGoSum()
797         if err != nil {
798                 base.Fatalf("%s", err)
799         }
800         if !inited {
801                 return
802         }
803
804         for m, hs := range goSum.m {
805                 if !keep[m] {
806                         for _, h := range hs {
807                                 goSum.status[modSum{m, h}] = modSumStatus{used: false, dirty: true}
808                         }
809                         goSum.overwrite = true
810                 }
811         }
812 }
813
814 const goSumMismatch = `
815
816 SECURITY ERROR
817 This download does NOT match an earlier download recorded in go.sum.
818 The bits may have been replaced on the origin server, or an attacker may
819 have intercepted the download attempt.
820
821 For more information, see 'go help module-auth'.
822 `
823
824 const sumdbMismatch = `
825
826 SECURITY ERROR
827 This download does NOT match the one reported by the checksum server.
828 The bits may have been replaced on the origin server, or an attacker may
829 have intercepted the download attempt.
830
831 For more information, see 'go help module-auth'.
832 `
833
834 const hashVersionMismatch = `
835
836 SECURITY WARNING
837 This download is listed in go.sum, but using an unknown hash algorithm.
838 The download cannot be verified.
839
840 For more information, see 'go help module-auth'.
841
842 `
843
844 var HelpModuleAuth = &base.Command{
845         UsageLine: "module-auth",
846         Short:     "module authentication using go.sum",
847         Long: `
848 When the go command downloads a module zip file or go.mod file into the
849 module cache, it computes a cryptographic hash and compares it with a known
850 value to verify the file hasn't changed since it was first downloaded. Known
851 hashes are stored in a file in the module root directory named go.sum. Hashes
852 may also be downloaded from the checksum database depending on the values of
853 GOSUMDB, GOPRIVATE, and GONOSUMDB.
854
855 For details, see https://golang.org/ref/mod#authenticating.
856 `,
857 }
858
859 var HelpPrivate = &base.Command{
860         UsageLine: "private",
861         Short:     "configuration for downloading non-public code",
862         Long: `
863 The go command defaults to downloading modules from the public Go module
864 mirror at proxy.golang.org. It also defaults to validating downloaded modules,
865 regardless of source, against the public Go checksum database at sum.golang.org.
866 These defaults work well for publicly available source code.
867
868 The GOPRIVATE environment variable controls which modules the go command
869 considers to be private (not available publicly) and should therefore not use
870 the proxy or checksum database. The variable is a comma-separated list of
871 glob patterns (in the syntax of Go's path.Match) of module path prefixes.
872 For example,
873
874         GOPRIVATE=*.corp.example.com,rsc.io/private
875
876 causes the go command to treat as private any module with a path prefix
877 matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
878 and rsc.io/private/quux.
879
880 For fine-grained control over module download and validation, the GONOPROXY
881 and GONOSUMDB environment variables accept the same kind of glob list
882 and override GOPRIVATE for the specific decision of whether to use the proxy
883 and checksum database, respectively.
884
885 For example, if a company ran a module proxy serving private modules,
886 users would configure go using:
887
888         GOPRIVATE=*.corp.example.com
889         GOPROXY=proxy.example.com
890         GONOPROXY=none
891
892 The GOPRIVATE variable is also used to define the "public" and "private"
893 patterns for the GOVCS variable; see 'go help vcs'. For that usage,
894 GOPRIVATE applies even in GOPATH mode. In that case, it matches import paths
895 instead of module paths.
896
897 The 'go env -w' command (see 'go help env') can be used to set these variables
898 for future go command invocations.
899
900 For more details, see https://golang.org/ref/mod#private-modules.
901 `,
902 }