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.
23 "cmd/go/internal/base"
25 "cmd/go/internal/lockedfile"
27 "cmd/go/internal/robustio"
28 "cmd/go/internal/trace"
30 "golang.org/x/mod/module"
31 "golang.org/x/mod/sumdb/dirhash"
32 modzip "golang.org/x/mod/zip"
35 var downloadCache par.Cache
37 // Download downloads the specific module version to the
38 // local download cache and returns the name of the directory
39 // corresponding to the root of the module's file tree.
40 func Download(ctx context.Context, mod module.Version) (dir string, err error) {
41 if err := checkCacheDir(); err != nil {
42 base.Fatalf("go: %v", err)
45 // The par.Cache here avoids duplicate work.
50 c := downloadCache.Do(mod, func() interface{} {
51 dir, err := download(ctx, mod)
53 return cached{"", err}
56 return cached{dir, nil}
61 func download(ctx context.Context, mod module.Version) (dir string, err error) {
62 ctx, span := trace.StartSpan(ctx, "modfetch.download "+mod.String())
65 dir, err = DownloadDir(mod)
67 // The directory has already been completely extracted (no .partial file exists).
69 } else if dir == "" || !errors.Is(err, fs.ErrNotExist) {
73 // To avoid cluttering the cache with extraneous files,
74 // DownloadZip uses the same lockfile as Download.
75 // Invoke DownloadZip before locking the file.
76 zipfile, err := DownloadZip(ctx, mod)
81 unlock, err := lockVersion(mod)
87 ctx, span = trace.StartSpan(ctx, "unzip "+zipfile)
90 // Check whether the directory was populated while we were waiting on the lock.
91 _, dirErr := DownloadDir(mod)
95 _, dirExists := dirErr.(*DownloadDirPartialError)
97 // Clean up any remaining temporary directories created by old versions
98 // (before 1.16), as well as partially extracted directories (indicated by
99 // DownloadDirPartialError, usually because of a .partial file). This is only
100 // safe to do because the lock file ensures that their writers are no longer
102 parentDir := filepath.Dir(dir)
103 tmpPrefix := filepath.Base(dir) + ".tmp-"
104 if old, err := filepath.Glob(filepath.Join(parentDir, tmpPrefix+"*")); err == nil {
105 for _, path := range old {
106 RemoveAll(path) // best effort
110 if err := RemoveAll(dir); err != nil {
115 partialPath, err := CachePath(mod, "partial")
120 // Extract the module zip directory at its final location.
122 // To prevent other processes from reading the directory if we crash,
123 // create a .partial file before extracting the directory, and delete
124 // the .partial file afterward (all while holding the lock).
126 // Before Go 1.16, we extracted to a temporary directory with a random name
127 // then renamed it into place with os.Rename. On Windows, this failed with
128 // ERROR_ACCESS_DENIED when another process (usually an anti-virus scanner)
129 // opened files in the temporary directory.
131 // Go 1.14.2 and higher respect .partial files. Older versions may use
132 // partially extracted directories. 'go mod verify' can detect this,
133 // and 'go clean -modcache' can fix it.
134 if err := os.MkdirAll(parentDir, 0777); err != nil {
137 if err := os.WriteFile(partialPath, nil, 0666); err != nil {
140 if err := modzip.Unzip(dir, mod, zipfile); err != nil {
141 fmt.Fprintf(os.Stderr, "-> %s\n", err)
142 if rmErr := RemoveAll(dir); rmErr == nil {
143 os.Remove(partialPath)
147 if err := os.Remove(partialPath); err != nil {
152 makeDirsReadOnly(dir)
157 var downloadZipCache par.Cache
159 // DownloadZip downloads the specific module version to the
160 // local zip cache and returns the name of the zip file.
161 func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err error) {
162 // The par.Cache here avoids duplicate work.
167 c := downloadZipCache.Do(mod, func() interface{} {
168 zipfile, err := CachePath(mod, "zip")
170 return cached{"", err}
172 ziphashfile := zipfile + "hash"
174 // Return without locking if the zip and ziphash files exist.
175 if _, err := os.Stat(zipfile); err == nil {
176 if _, err := os.Stat(ziphashfile); err == nil {
177 return cached{zipfile, nil}
181 // The zip or ziphash file does not exist. Acquire the lock and create them.
182 if cfg.CmdName != "mod download" {
183 fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version)
185 unlock, err := lockVersion(mod)
187 return cached{"", err}
191 if err := downloadZip(ctx, mod, zipfile); err != nil {
192 return cached{"", err}
194 return cached{zipfile, nil}
196 return c.zipfile, c.err
199 func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err error) {
200 ctx, span := trace.StartSpan(ctx, "modfetch.downloadZip "+zipfile)
203 // Double-check that the zipfile was not created while we were waiting for
204 // the lock in DownloadZip.
205 ziphashfile := zipfile + "hash"
206 var zipExists, ziphashExists bool
207 if _, err := os.Stat(zipfile); err == nil {
210 if _, err := os.Stat(ziphashfile); err == nil {
213 if zipExists && ziphashExists {
217 // Create parent directories.
218 if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
222 // Clean up any remaining tempfiles from previous runs.
223 // This is only safe to do because the lock file ensures that their
224 // writers are no longer active.
225 tmpPattern := filepath.Base(zipfile) + "*.tmp"
226 if old, err := filepath.Glob(filepath.Join(filepath.Dir(zipfile), tmpPattern)); err == nil {
227 for _, path := range old {
228 os.Remove(path) // best effort
232 // If the zip file exists, the ziphash file must have been deleted
233 // or lost after a file system crash. Re-hash the zip without downloading.
235 return hashZip(mod, zipfile, ziphashfile)
238 // From here to the os.Rename call below is functionally almost equivalent to
239 // renameio.WriteToFile, with one key difference: we want to validate the
240 // contents of the file (by hashing it) before we commit it. Because the file
241 // is zip-compressed, we need an actual file — or at least an io.ReaderAt — to
242 // validate it: we can't just tee the stream as we write it.
243 f, err := os.CreateTemp(filepath.Dir(zipfile), tmpPattern)
254 var unrecoverableErr error
255 err = TryProxies(func(proxy string) error {
256 if unrecoverableErr != nil {
257 return unrecoverableErr
259 repo := Lookup(proxy, mod.Path)
260 err := repo.Zip(f, mod.Version)
262 // Zip may have partially written to f before failing.
263 // (Perhaps the server crashed while sending the file?)
264 // Since we allow fallback on error in some cases, we need to fix up the
265 // file to be empty again for the next attempt.
266 if _, err := f.Seek(0, io.SeekStart); err != nil {
267 unrecoverableErr = err
270 if err := f.Truncate(0); err != nil {
271 unrecoverableErr = err
281 // Double-check that the paths within the zip file are well-formed.
283 // TODO(bcmills): There is a similar check within the Unzip function. Can we eliminate one?
288 z, err := zip.NewReader(f, fi.Size())
292 prefix := mod.Path + "@" + mod.Version + "/"
293 for _, f := range z.File {
294 if !strings.HasPrefix(f.Name, prefix) {
295 return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
299 if err := f.Close(); err != nil {
303 // Hash the zip file and check the sum before renaming to the final location.
304 if err := hashZip(mod, f.Name(), ziphashfile); err != nil {
307 if err := os.Rename(f.Name(), zipfile); err != nil {
311 // TODO(bcmills): Should we make the .zip and .ziphash files read-only to discourage tampering?
316 // hashZip reads the zip file opened in f, then writes the hash to ziphashfile,
317 // overwriting that file if it exists.
319 // If the hash does not match go.sum (or the sumdb if enabled), hashZip returns
320 // an error and does not write ziphashfile.
321 func hashZip(mod module.Version, zipfile, ziphashfile string) error {
322 hash, err := dirhash.HashZip(zipfile, dirhash.DefaultHash)
326 if err := checkModSum(mod, hash); err != nil {
329 hf, err := lockedfile.Create(ziphashfile)
333 if err := hf.Truncate(int64(len(hash))); err != nil {
336 if _, err := hf.WriteAt([]byte(hash), 0); err != nil {
339 if err := hf.Close(); err != nil {
346 // makeDirsReadOnly makes a best-effort attempt to remove write permissions for dir
347 // and its transitive contents.
348 func makeDirsReadOnly(dir string) {
349 type pathMode struct {
353 var dirs []pathMode // in lexical order
354 filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
355 if err == nil && d.IsDir() {
356 info, err := d.Info()
357 if err == nil && info.Mode()&0222 != 0 {
358 dirs = append(dirs, pathMode{path, info.Mode()})
364 // Run over list backward to chmod children before parents.
365 for i := len(dirs) - 1; i >= 0; i-- {
366 os.Chmod(dirs[i].path, dirs[i].mode&^0222)
370 // RemoveAll removes a directory written by Download or Unzip, first applying
371 // any permission changes needed to do so.
372 func RemoveAll(dir string) error {
373 // Module cache has 0555 directories; make them writable in order to remove content.
374 filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error {
376 return nil // ignore errors walking in file system
383 return robustio.RemoveAll(dir)
386 var GoSumFile string // path to go.sum; set by package modload
395 m map[module.Version][]string // content of go.sum file
396 status map[modSum]modSumStatus // state of sums in m
397 overwrite bool // if true, overwrite go.sum without incorporating its contents
398 enabled bool // whether to use go.sum at all
401 type modSumStatus struct {
405 // initGoSum initializes the go.sum data.
406 // The boolean it returns reports whether the
407 // use of go.sum is now enabled.
408 // The goSum lock must be held.
409 func initGoSum() (bool, error) {
417 goSum.m = make(map[module.Version][]string)
418 goSum.status = make(map[modSum]modSumStatus)
419 data, err := lockedfile.Read(GoSumFile)
420 if err != nil && !os.IsNotExist(err) {
424 readGoSum(goSum.m, GoSumFile, data)
429 // emptyGoModHash is the hash of a 1-file tree containing a 0-length go.mod.
430 // A bug caused us to write these into go.sum files for non-modules.
431 // We detect and remove them.
432 const emptyGoModHash = "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY="
434 // readGoSum parses data, which is the content of file,
435 // and adds it to goSum.m. The goSum lock must be held.
436 func readGoSum(dst map[module.Version][]string, file string, data []byte) error {
441 i := bytes.IndexByte(data, '\n')
443 line, data = data, nil
445 line, data = data[:i], data[i+1:]
447 f := strings.Fields(string(line))
449 // blank line; skip it
453 return fmt.Errorf("malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
455 if f[2] == emptyGoModHash {
459 mod := module.Version{Path: f[0], Version: f[1]}
460 dst[mod] = append(dst[mod], f[2])
465 // HaveSum returns true if the go.sum file contains an entry for mod.
466 // The entry's hash must be generated with a known hash algorithm.
467 // mod.Version may have a "/go.mod" suffix to distinguish sums for
468 // .mod and .zip files.
469 func HaveSum(mod module.Version) bool {
471 defer goSum.mu.Unlock()
472 inited, err := initGoSum()
473 if err != nil || !inited {
476 for _, h := range goSum.m[mod] {
477 if !strings.HasPrefix(h, "h1:") {
480 if !goSum.status[modSum{mod, h}].dirty {
487 // checkMod checks the given module's checksum.
488 func checkMod(mod module.Version) {
489 // Do the file I/O before acquiring the go.sum lock.
490 ziphash, err := CachePath(mod, "ziphash")
492 base.Fatalf("verifying %v", module.VersionError(mod, err))
494 data, err := lockedfile.Read(ziphash)
496 base.Fatalf("verifying %v", module.VersionError(mod, err))
498 data = bytes.TrimSpace(data)
499 if !isValidSum(data) {
500 // Recreate ziphash file from zip file and use that to check the mod sum.
501 zip, err := CachePath(mod, "zip")
503 base.Fatalf("verifying %v", module.VersionError(mod, err))
505 err = hashZip(mod, zip, ziphash)
507 base.Fatalf("verifying %v", module.VersionError(mod, err))
512 if !strings.HasPrefix(h, "h1:") {
513 base.Fatalf("verifying %v", module.VersionError(mod, fmt.Errorf("unexpected ziphash: %q", h)))
516 if err := checkModSum(mod, h); err != nil {
517 base.Fatalf("%s", err)
521 // goModSum returns the checksum for the go.mod contents.
522 func goModSum(data []byte) (string, error) {
523 return dirhash.Hash1([]string{"go.mod"}, func(string) (io.ReadCloser, error) {
524 return io.NopCloser(bytes.NewReader(data)), nil
528 // checkGoMod checks the given module's go.mod checksum;
529 // data is the go.mod content.
530 func checkGoMod(path, version string, data []byte) error {
531 h, err := goModSum(data)
533 return &module.ModuleError{Path: path, Version: version, Err: fmt.Errorf("verifying go.mod: %v", err)}
536 return checkModSum(module.Version{Path: path, Version: version + "/go.mod"}, h)
539 // checkModSum checks that the recorded checksum for mod is h.
541 // mod.Version may have the additional suffix "/go.mod" to request the checksum
542 // for the module's go.mod file only.
543 func checkModSum(mod module.Version, h string) error {
544 // We lock goSum when manipulating it,
545 // but we arrange to release the lock when calling checkSumDB,
546 // so that parallel calls to checkModHash can execute parallel calls
549 // Check whether mod+h is listed in go.sum already. If so, we're done.
551 inited, err := initGoSum()
556 done := inited && haveModSumLocked(mod, h)
558 st := goSum.status[modSum{mod, h}]
560 goSum.status[modSum{mod, h}] = st
568 // Not listed, so we want to add them.
569 // Consult checksum database if appropriate.
571 // Calls base.Fatalf if mismatch detected.
572 if err := checkSumDB(mod, h); err != nil {
577 // Add mod+h to go.sum, if it hasn't appeared already.
580 addModSumLocked(mod, h)
581 st := goSum.status[modSum{mod, h}]
583 goSum.status[modSum{mod, h}] = st
589 // haveModSumLocked reports whether the pair mod,h is already listed in go.sum.
590 // If it finds a conflicting pair instead, it calls base.Fatalf.
591 // goSum.mu must be locked.
592 func haveModSumLocked(mod module.Version, h string) bool {
593 for _, vh := range goSum.m[mod] {
597 if strings.HasPrefix(vh, "h1:") {
598 base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v"+goSumMismatch, mod.Path, mod.Version, h, vh)
604 // addModSumLocked adds the pair mod,h to go.sum.
605 // goSum.mu must be locked.
606 func addModSumLocked(mod module.Version, h string) {
607 if haveModSumLocked(mod, h) {
610 if len(goSum.m[mod]) > 0 {
611 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)
613 goSum.m[mod] = append(goSum.m[mod], h)
616 // checkSumDB checks the mod, h pair against the Go checksum database.
617 // It calls base.Fatalf if the hash is to be rejected.
618 func checkSumDB(mod module.Version, h string) error {
619 modWithoutSuffix := mod
621 if strings.HasSuffix(mod.Version, "/go.mod") {
623 modWithoutSuffix.Version = strings.TrimSuffix(mod.Version, "/go.mod")
626 db, lines, err := lookupSumDB(mod)
628 return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: %v", noun, err))
631 have := mod.Path + " " + mod.Version + " " + h
632 prefix := mod.Path + " " + mod.Version + " h1:"
633 for _, line := range lines {
637 if strings.HasPrefix(line, prefix) {
638 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:"):]))
644 // Sum returns the checksum for the downloaded copy of the given module,
645 // if present in the download cache.
646 func Sum(mod module.Version) string {
647 if cfg.GOMODCACHE == "" {
648 // Do not use current directory.
652 ziphash, err := CachePath(mod, "ziphash")
656 data, err := lockedfile.Read(ziphash)
660 data = bytes.TrimSpace(data)
661 if !isValidSum(data) {
667 // isValidSum returns true if data is the valid contents of a zip hash file.
668 // Certain critical files are written to disk by first truncating
669 // then writing the actual bytes, so that if the write fails
670 // the corrupt file should contain at least one of the null
671 // bytes written by the truncate operation.
672 func isValidSum(data []byte) bool {
673 if bytes.IndexByte(data, '\000') >= 0 {
677 if len(data) != len("h1:")+base64.StdEncoding.EncodedLen(sha256.Size) {
684 var ErrGoSumDirty = errors.New("updates to go.sum needed, disabled by -mod=readonly")
686 // WriteGoSum writes the go.sum file if it needs to be updated.
688 // keep is used to check whether a newly added sum should be saved in go.sum.
689 // It should have entries for both module content sums and go.mod sums
690 // (version ends with "/go.mod"). Existing sums will be preserved unless they
691 // have been marked for deletion with TrimGoSum.
692 func WriteGoSum(keep map[module.Version]bool, readonly bool) error {
694 defer goSum.mu.Unlock()
696 // If we haven't read the go.sum file yet, don't bother writing it.
701 // Check whether we need to add sums for which keep[m] is true or remove
702 // unused sums marked with TrimGoSum. If there are no changes to make,
703 // just return without opening go.sum.
706 for m, hs := range goSum.m {
707 for _, h := range hs {
708 st := goSum.status[modSum{m, h}]
709 if st.dirty && (!st.used || keep[m]) {
722 // Make a best-effort attempt to acquire the side lock, only to exclude
723 // previous versions of the 'go' command from making simultaneous edits.
724 if unlock, err := SideLock(); err == nil {
728 err := lockedfile.Transform(GoSumFile, func(data []byte) ([]byte, error) {
729 if !goSum.overwrite {
730 // Incorporate any sums added by other processes in the meantime.
731 // Add only the sums that we actually checked: the user may have edited or
732 // truncated the file to remove erroneous hashes, and we shouldn't restore
733 // them without good reason.
734 goSum.m = make(map[module.Version][]string, len(goSum.m))
735 readGoSum(goSum.m, GoSumFile, data)
736 for ms, st := range goSum.status {
738 addModSumLocked(ms.mod, ms.sum)
743 var mods []module.Version
744 for m := range goSum.m {
745 mods = append(mods, m)
750 for _, m := range mods {
753 for _, h := range list {
754 st := goSum.status[modSum{m, h}]
755 if !st.dirty || (st.used && keep[m]) {
756 fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
760 return buf.Bytes(), nil
764 return fmt.Errorf("updating go.sum: %w", err)
767 goSum.status = make(map[modSum]modSumStatus)
768 goSum.overwrite = false
772 // TrimGoSum trims go.sum to contain only the modules needed for reproducible
775 // keep is used to check whether a sum should be retained in go.mod. It should
776 // have entries for both module content sums and go.mod sums (version ends
778 func TrimGoSum(keep map[module.Version]bool) {
780 defer goSum.mu.Unlock()
781 inited, err := initGoSum()
783 base.Fatalf("%s", err)
789 for m, hs := range goSum.m {
791 for _, h := range hs {
792 goSum.status[modSum{m, h}] = modSumStatus{used: false, dirty: true}
794 goSum.overwrite = true
799 const goSumMismatch = `
802 This download does NOT match an earlier download recorded in go.sum.
803 The bits may have been replaced on the origin server, or an attacker may
804 have intercepted the download attempt.
806 For more information, see 'go help module-auth'.
809 const sumdbMismatch = `
812 This download does NOT match the one reported by the checksum server.
813 The bits may have been replaced on the origin server, or an attacker may
814 have intercepted the download attempt.
816 For more information, see 'go help module-auth'.
819 const hashVersionMismatch = `
822 This download is listed in go.sum, but using an unknown hash algorithm.
823 The download cannot be verified.
825 For more information, see 'go help module-auth'.
829 var HelpModuleAuth = &base.Command{
830 UsageLine: "module-auth",
831 Short: "module authentication using go.sum",
833 When the go command downloads a module zip file or go.mod file into the
834 module cache, it computes a cryptographic hash and compares it with a known
835 value to verify the file hasn't changed since it was first downloaded. Known
836 hashes are stored in a file in the module root directory named go.sum. Hashes
837 may also be downloaded from the checksum database depending on the values of
838 GOSUMDB, GOPRIVATE, and GONOSUMDB.
840 For details, see https://golang.org/ref/mod#authenticating.
844 var HelpPrivate = &base.Command{
845 UsageLine: "private",
846 Short: "configuration for downloading non-public code",
848 The go command defaults to downloading modules from the public Go module
849 mirror at proxy.golang.org. It also defaults to validating downloaded modules,
850 regardless of source, against the public Go checksum database at sum.golang.org.
851 These defaults work well for publicly available source code.
853 The GOPRIVATE environment variable controls which modules the go command
854 considers to be private (not available publicly) and should therefore not use
855 the proxy or checksum database. The variable is a comma-separated list of
856 glob patterns (in the syntax of Go's path.Match) of module path prefixes.
859 GOPRIVATE=*.corp.example.com,rsc.io/private
861 causes the go command to treat as private any module with a path prefix
862 matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
863 and rsc.io/private/quux.
865 For fine-grained control over module download and validation, the GONOPROXY
866 and GONOSUMDB environment variables accept the same kind of glob list
867 and override GOPRIVATE for the specific decision of whether to use the proxy
868 and checksum database, respectively.
870 For example, if a company ran a module proxy serving private modules,
871 users would configure go using:
873 GOPRIVATE=*.corp.example.com
874 GOPROXY=proxy.example.com
877 The GOPRIVATE variable is also used to define the "public" and "private"
878 patterns for the GOVCS variable; see 'go help vcs'. For that usage,
879 GOPRIVATE applies even in GOPATH mode. In that case, it matches import paths
880 instead of module paths.
882 The 'go env -w' command (see 'go help env') can be used to set these variables
883 for future go command invocations.
885 For more details, see https://golang.org/ref/mod#private-modules.