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/fsys"
26 "cmd/go/internal/lockedfile"
28 "cmd/go/internal/robustio"
29 "cmd/go/internal/trace"
31 "golang.org/x/mod/module"
32 "golang.org/x/mod/sumdb/dirhash"
33 modzip "golang.org/x/mod/zip"
36 var downloadCache par.Cache
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)
46 // The par.Cache here avoids duplicate work.
51 c := downloadCache.Do(mod, func() interface{} {
52 dir, err := download(ctx, mod)
54 return cached{"", err}
57 return cached{dir, nil}
62 func download(ctx context.Context, mod module.Version) (dir string, err error) {
63 ctx, span := trace.StartSpan(ctx, "modfetch.download "+mod.String())
66 dir, err = DownloadDir(mod)
68 // The directory has already been completely extracted (no .partial file exists).
70 } else if dir == "" || !errors.Is(err, fs.ErrNotExist) {
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)
82 unlock, err := lockVersion(mod)
88 ctx, span = trace.StartSpan(ctx, "unzip "+zipfile)
91 // Check whether the directory was populated while we were waiting on the lock.
92 _, dirErr := DownloadDir(mod)
96 _, dirExists := dirErr.(*DownloadDirPartialError)
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
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
111 if err := RemoveAll(dir); err != nil {
116 partialPath, err := CachePath(mod, "partial")
121 // Extract the module zip directory at its final location.
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).
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.
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 {
138 if err := os.WriteFile(partialPath, nil, 0666); err != nil {
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)
148 if err := os.Remove(partialPath); err != nil {
153 makeDirsReadOnly(dir)
158 var downloadZipCache par.Cache
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.
168 c := downloadZipCache.Do(mod, func() interface{} {
169 zipfile, err := CachePath(mod, "zip")
171 return cached{"", err}
173 ziphashfile := zipfile + "hash"
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}
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)
186 unlock, err := lockVersion(mod)
188 return cached{"", err}
192 if err := downloadZip(ctx, mod, zipfile); err != nil {
193 return cached{"", err}
195 return cached{zipfile, nil}
197 return c.zipfile, c.err
200 func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err error) {
201 ctx, span := trace.StartSpan(ctx, "modfetch.downloadZip "+zipfile)
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 {
211 if _, err := os.Stat(ziphashfile); err == nil {
214 if zipExists && ziphashExists {
218 // Create parent directories.
219 if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
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
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.
236 return hashZip(mod, zipfile, ziphashfile)
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)
255 var unrecoverableErr error
256 err = TryProxies(func(proxy string) error {
257 if unrecoverableErr != nil {
258 return unrecoverableErr
260 repo := Lookup(proxy, mod.Path)
261 err := repo.Zip(f, mod.Version)
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
271 if err := f.Truncate(0); err != nil {
272 unrecoverableErr = err
282 // Double-check that the paths within the zip file are well-formed.
284 // TODO(bcmills): There is a similar check within the Unzip function. Can we eliminate one?
289 z, err := zip.NewReader(f, fi.Size())
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)
300 if err := f.Close(); err != nil {
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 {
308 if err := os.Rename(f.Name(), zipfile); err != nil {
312 // TODO(bcmills): Should we make the .zip and .ziphash files read-only to discourage tampering?
317 // hashZip reads the zip file opened in f, then writes the hash to ziphashfile,
318 // overwriting that file if it exists.
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)
327 if err := checkModSum(mod, hash); err != nil {
330 hf, err := lockedfile.Create(ziphashfile)
334 if err := hf.Truncate(int64(len(hash))); err != nil {
337 if _, err := hf.WriteAt([]byte(hash), 0); err != nil {
340 if err := hf.Close(); err != nil {
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 {
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()})
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)
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 {
377 return nil // ignore errors walking in file system
384 return robustio.RemoveAll(dir)
387 var GoSumFile string // path to go.sum; set by package modload
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
402 type modSumStatus struct {
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) {
418 goSum.m = make(map[module.Version][]string)
419 goSum.status = make(map[modSum]modSumStatus)
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)
430 data, err = lockedfile.Read(GoSumFile)
432 if err != nil && !os.IsNotExist(err) {
436 readGoSum(goSum.m, GoSumFile, data)
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="
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 {
453 i := bytes.IndexByte(data, '\n')
455 line, data = data, nil
457 line, data = data[:i], data[i+1:]
459 f := strings.Fields(string(line))
461 // blank line; skip it
465 return fmt.Errorf("malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
467 if f[2] == emptyGoModHash {
471 mod := module.Version{Path: f[0], Version: f[1]}
472 dst[mod] = append(dst[mod], f[2])
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 {
483 defer goSum.mu.Unlock()
484 inited, err := initGoSum()
485 if err != nil || !inited {
488 for _, h := range goSum.m[mod] {
489 if !strings.HasPrefix(h, "h1:") {
492 if !goSum.status[modSum{mod, h}].dirty {
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")
504 base.Fatalf("verifying %v", module.VersionError(mod, err))
506 data, err := lockedfile.Read(ziphash)
508 base.Fatalf("verifying %v", module.VersionError(mod, err))
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")
515 base.Fatalf("verifying %v", module.VersionError(mod, err))
517 err = hashZip(mod, zip, ziphash)
519 base.Fatalf("verifying %v", module.VersionError(mod, err))
524 if !strings.HasPrefix(h, "h1:") {
525 base.Fatalf("verifying %v", module.VersionError(mod, fmt.Errorf("unexpected ziphash: %q", h)))
528 if err := checkModSum(mod, h); err != nil {
529 base.Fatalf("%s", err)
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
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)
545 return &module.ModuleError{Path: path, Version: version, Err: fmt.Errorf("verifying go.mod: %v", err)}
548 return checkModSum(module.Version{Path: path, Version: version + "/go.mod"}, h)
551 // checkModSum checks that the recorded checksum for mod is h.
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
561 // Check whether mod+h is listed in go.sum already. If so, we're done.
563 inited, err := initGoSum()
568 done := inited && haveModSumLocked(mod, h)
570 st := goSum.status[modSum{mod, h}]
572 goSum.status[modSum{mod, h}] = st
580 // Not listed, so we want to add them.
581 // Consult checksum database if appropriate.
583 // Calls base.Fatalf if mismatch detected.
584 if err := checkSumDB(mod, h); err != nil {
589 // Add mod+h to go.sum, if it hasn't appeared already.
592 addModSumLocked(mod, h)
593 st := goSum.status[modSum{mod, h}]
595 goSum.status[modSum{mod, h}] = st
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] {
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)
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) {
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)
625 goSum.m[mod] = append(goSum.m[mod], h)
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
633 if strings.HasSuffix(mod.Version, "/go.mod") {
635 modWithoutSuffix.Version = strings.TrimSuffix(mod.Version, "/go.mod")
638 db, lines, err := lookupSumDB(mod)
640 return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: %v", noun, err))
643 have := mod.Path + " " + mod.Version + " " + h
644 prefix := mod.Path + " " + mod.Version + " h1:"
645 for _, line := range lines {
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:"):]))
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.
664 ziphash, err := CachePath(mod, "ziphash")
668 data, err := lockedfile.Read(ziphash)
672 data = bytes.TrimSpace(data)
673 if !isValidSum(data) {
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 {
689 if len(data) != len("h1:")+base64.StdEncoding.EncodedLen(sha256.Size) {
696 var ErrGoSumDirty = errors.New("updates to go.sum needed, disabled by -mod=readonly")
698 // WriteGoSum writes the go.sum file if it needs to be updated.
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 {
706 defer goSum.mu.Unlock()
708 // If we haven't read the go.sum file yet, don't bother writing it.
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.
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]) {
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")
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 {
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 {
753 addModSumLocked(ms.mod, ms.sum)
758 var mods []module.Version
759 for m := range goSum.m {
760 mods = append(mods, m)
765 for _, m := range mods {
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)
775 return buf.Bytes(), nil
779 return fmt.Errorf("updating go.sum: %w", err)
782 goSum.status = make(map[modSum]modSumStatus)
783 goSum.overwrite = false
787 // TrimGoSum trims go.sum to contain only the modules needed for reproducible
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
793 func TrimGoSum(keep map[module.Version]bool) {
795 defer goSum.mu.Unlock()
796 inited, err := initGoSum()
798 base.Fatalf("%s", err)
804 for m, hs := range goSum.m {
806 for _, h := range hs {
807 goSum.status[modSum{m, h}] = modSumStatus{used: false, dirty: true}
809 goSum.overwrite = true
814 const goSumMismatch = `
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.
821 For more information, see 'go help module-auth'.
824 const sumdbMismatch = `
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.
831 For more information, see 'go help module-auth'.
834 const hashVersionMismatch = `
837 This download is listed in go.sum, but using an unknown hash algorithm.
838 The download cannot be verified.
840 For more information, see 'go help module-auth'.
844 var HelpModuleAuth = &base.Command{
845 UsageLine: "module-auth",
846 Short: "module authentication using go.sum",
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.
855 For details, see https://golang.org/ref/mod#authenticating.
859 var HelpPrivate = &base.Command{
860 UsageLine: "private",
861 Short: "configuration for downloading non-public code",
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.
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.
874 GOPRIVATE=*.corp.example.com,rsc.io/private
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.
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.
885 For example, if a company ran a module proxy serving private modules,
886 users would configure go using:
888 GOPRIVATE=*.corp.example.com
889 GOPROXY=proxy.example.com
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.
897 The 'go env -w' command (see 'go help env') can be used to set these variables
898 for future go command invocations.
900 For more details, see https://golang.org/ref/mod#private-modules.