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 // WriteGoSum writes the go.sum file if it needs to be updated.
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) {
704 defer goSum.mu.Unlock()
706 // If we haven't read the go.sum file yet, don't bother writing it.
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.
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]) {
728 if cfg.BuildMod == "readonly" {
729 base.Fatalf("go: updates to go.sum needed, disabled by -mod=readonly")
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")
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 {
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 {
751 addModSumLocked(ms.mod, ms.sum)
756 var mods []module.Version
757 for m := range goSum.m {
758 mods = append(mods, m)
763 for _, m := range mods {
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)
773 return buf.Bytes(), nil
777 base.Fatalf("go: updating go.sum: %v", err)
780 goSum.status = make(map[modSum]modSumStatus)
781 goSum.overwrite = false
784 // TrimGoSum trims go.sum to contain only the modules needed for reproducible
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
790 func TrimGoSum(keep map[module.Version]bool) {
792 defer goSum.mu.Unlock()
793 inited, err := initGoSum()
795 base.Fatalf("%s", err)
801 for m, hs := range goSum.m {
803 for _, h := range hs {
804 goSum.status[modSum{m, h}] = modSumStatus{used: false, dirty: true}
806 goSum.overwrite = true
811 const goSumMismatch = `
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.
818 For more information, see 'go help module-auth'.
821 const sumdbMismatch = `
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.
828 For more information, see 'go help module-auth'.
831 const hashVersionMismatch = `
834 This download is listed in go.sum, but using an unknown hash algorithm.
835 The download cannot be verified.
837 For more information, see 'go help module-auth'.
841 var HelpModuleAuth = &base.Command{
842 UsageLine: "module-auth",
843 Short: "module authentication using go.sum",
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.
852 For details, see https://golang.org/ref/mod#authenticating.
856 var HelpPrivate = &base.Command{
857 UsageLine: "private",
858 Short: "configuration for downloading non-public code",
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.
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.
871 GOPRIVATE=*.corp.example.com,rsc.io/private
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.
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.
882 For example, if a company ran a module proxy serving private modules,
883 users would configure go using:
885 GOPRIVATE=*.corp.example.com
886 GOPROXY=proxy.example.com
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.
894 The 'go env -w' command (see 'go help env') can be used to set these variables
895 for future go command invocations.
897 For more details, see https://golang.org/ref/mod#private-modules.