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.
19 "cmd/go/internal/base"
21 "cmd/go/internal/dirhash"
22 "cmd/go/internal/module"
26 var downloadCache par
.Cache
28 // Download downloads the specific module version to the
29 // local download cache and returns the name of the directory
30 // corresponding to the root of the module's file tree.
31 func Download(mod module
.Version
) (dir
string, err error
) {
33 // Do not download to current directory.
34 return "", fmt
.Errorf("missing modfetch.PkgMod")
37 // The par.Cache here avoids duplicate work but also
38 // avoids conflicts from simultaneous calls by multiple goroutines
39 // for the same version.
44 c
:= downloadCache
.Do(mod
, func() interface{} {
45 dir
, err
:= DownloadDir(mod
)
47 return cached
{"", err
}
49 if files
, _
:= ioutil
.ReadDir(dir
); len(files
) == 0 {
50 zipfile
, err
:= DownloadZip(mod
)
52 return cached
{"", err
}
54 modpath
:= mod
.Path
+ "@" + mod
.Version
55 if err
:= Unzip(dir
, zipfile
, modpath
, 0); err
!= nil {
56 fmt
.Fprintf(os
.Stderr
, "-> %s\n", err
)
57 return cached
{"", err
}
61 return cached
{dir
, nil}
66 var downloadZipCache par
.Cache
68 // DownloadZip downloads the specific module version to the
69 // local zip cache and returns the name of the zip file.
70 func DownloadZip(mod module
.Version
) (zipfile
string, err error
) {
71 // The par.Cache here avoids duplicate work but also
72 // avoids conflicts from simultaneous calls by multiple goroutines
73 // for the same version.
78 c
:= downloadZipCache
.Do(mod
, func() interface{} {
79 zipfile
, err
:= CachePath(mod
, "zip")
81 return cached
{"", err
}
83 if _
, err
:= os
.Stat(zipfile
); err
== nil {
85 // This should only happen if the mod/cache directory is preinitialized
86 // or if pkg/mod/path was removed but not pkg/mod/cache/download.
87 if cfg
.CmdName
!= "mod download" {
88 fmt
.Fprintf(os
.Stderr
, "go: extracting %s %s\n", mod
.Path
, mod
.Version
)
91 if err
:= os
.MkdirAll(filepath
.Dir(zipfile
), 0777); err
!= nil {
92 return cached
{"", err
}
94 if cfg
.CmdName
!= "mod download" {
95 fmt
.Fprintf(os
.Stderr
, "go: downloading %s %s\n", mod
.Path
, mod
.Version
)
97 if err
:= downloadZip(mod
, zipfile
); err
!= nil {
98 return cached
{"", err
}
101 return cached
{zipfile
, nil}
103 return c
.zipfile
, c
.err
106 func downloadZip(mod module
.Version
, target
string) error
{
107 repo
, err
:= Lookup(mod
.Path
)
111 tmpfile
, err
:= repo
.Zip(mod
.Version
, os
.TempDir())
115 defer os
.Remove(tmpfile
)
117 // Double-check zip file looks OK.
118 z
, err
:= zip
.OpenReader(tmpfile
)
122 prefix
:= mod
.Path
+ "@" + mod
.Version
123 for _
, f
:= range z
.File
{
124 if !strings
.HasPrefix(f
.Name
, prefix
) {
126 return fmt
.Errorf("zip for %s has unexpected file %s", prefix
[:len(prefix
)-1], f
.Name
)
131 hash
, err
:= dirhash
.HashZip(tmpfile
, dirhash
.DefaultHash
)
135 checkOneSum(mod
, hash
) // check before installing the zip file
136 r
, err
:= os
.Open(tmpfile
)
141 w
, err
:= os
.Create(target
)
145 if _
, err
:= io
.Copy(w
, r
); err
!= nil {
147 return fmt
.Errorf("copying: %v", err
)
149 if err
:= w
.Close(); err
!= nil {
152 return ioutil
.WriteFile(target
+"hash", []byte(hash
), 0666)
155 var GoSumFile
string // path to go.sum; set by package modload
159 m
map[module
.Version
][]string // content of go.sum file (+ go.modverify if present)
160 enabled
bool // whether to use go.sum at all
161 modverify
string // path to go.modverify, to be deleted
164 // initGoSum initializes the go.sum data.
165 // It reports whether use of go.sum is now enabled.
166 // The goSum lock must be held.
167 func initGoSum() bool {
175 goSum
.m
= make(map[module
.Version
][]string)
176 data
, err
:= ioutil
.ReadFile(GoSumFile
)
177 if err
!= nil && !os
.IsNotExist(err
) {
178 base
.Fatalf("go: %v", err
)
181 readGoSum(GoSumFile
, data
)
183 // Add old go.modverify file.
184 // We'll delete go.modverify in WriteGoSum.
185 alt
:= strings
.TrimSuffix(GoSumFile
, ".sum") + ".modverify"
186 if data
, err
:= ioutil
.ReadFile(alt
); err
== nil {
188 goSum
.modverify
= alt
193 // emptyGoModHash is the hash of a 1-file tree containing a 0-length go.mod.
194 // A bug caused us to write these into go.sum files for non-modules.
195 // We detect and remove them.
196 const emptyGoModHash
= "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY="
198 // readGoSum parses data, which is the content of file,
199 // and adds it to goSum.m. The goSum lock must be held.
200 func readGoSum(file
string, data
[]byte) {
205 i
:= bytes
.IndexByte(data
, '\n')
207 line
, data
= data
, nil
209 line
, data
= data
[:i
], data
[i
+1:]
211 f
:= strings
.Fields(string(line
))
213 // blank line; skip it
217 base
.Fatalf("go: malformed go.sum:\n%s:%d: wrong number of fields %v", file
, lineno
, len(f
))
219 if f
[2] == emptyGoModHash
{
223 mod
:= module
.Version
{Path
: f
[0], Version
: f
[1]}
224 goSum
.m
[mod
] = append(goSum
.m
[mod
], f
[2])
228 // checkSum checks the given module's checksum.
229 func checkSum(mod module
.Version
) {
231 // Do not use current directory.
235 // Do the file I/O before acquiring the go.sum lock.
236 ziphash
, err
:= CachePath(mod
, "ziphash")
238 base
.Fatalf("go: verifying %s@%s: %v", mod
.Path
, mod
.Version
, err
)
240 data
, err
:= ioutil
.ReadFile(ziphash
)
242 if os
.IsNotExist(err
) {
243 // This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes.
246 base
.Fatalf("go: verifying %s@%s: %v", mod
.Path
, mod
.Version
, err
)
248 h
:= strings
.TrimSpace(string(data
))
249 if !strings
.HasPrefix(h
, "h1:") {
250 base
.Fatalf("go: verifying %s@%s: unexpected ziphash: %q", mod
.Path
, mod
.Version
, h
)
256 // goModSum returns the checksum for the go.mod contents.
257 func goModSum(data
[]byte) (string, error
) {
258 return dirhash
.Hash1([]string{"go.mod"}, func(string) (io
.ReadCloser
, error
) {
259 return ioutil
.NopCloser(bytes
.NewReader(data
)), nil
263 // checkGoMod checks the given module's go.mod checksum;
264 // data is the go.mod content.
265 func checkGoMod(path
, version
string, data
[]byte) {
266 h
, err
:= goModSum(data
)
268 base
.Fatalf("go: verifying %s %s go.mod: %v", path
, version
, err
)
271 checkOneSum(module
.Version
{Path
: path
, Version
: version
+ "/go.mod"}, h
)
274 // checkOneSum checks that the recorded hash for mod is h.
275 func checkOneSum(mod module
.Version
, h
string) {
277 defer goSum
.mu
.Unlock()
282 for _
, vh
:= range goSum
.m
[mod
] {
286 if strings
.HasPrefix(vh
, "h1:") {
287 base
.Fatalf("go: verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v", mod
.Path
, mod
.Version
, h
, vh
)
290 if len(goSum
.m
[mod
]) > 0 {
291 fmt
.Fprintf(os
.Stderr
, "warning: verifying %s@%s: unknown hashes in go.sum: %v; adding %v", mod
.Path
, mod
.Version
, strings
.Join(goSum
.m
[mod
], ", "), h
)
293 goSum
.m
[mod
] = append(goSum
.m
[mod
], h
)
296 // Sum returns the checksum for the downloaded copy of the given module,
297 // if present in the download cache.
298 func Sum(mod module
.Version
) string {
300 // Do not use current directory.
304 ziphash
, err
:= CachePath(mod
, "ziphash")
308 data
, err
:= ioutil
.ReadFile(ziphash
)
312 return strings
.TrimSpace(string(data
))
315 // WriteGoSum writes the go.sum file if it needs to be updated.
318 defer goSum
.mu
.Unlock()
323 var mods
[]module
.Version
324 for m
:= range goSum
.m
{
325 mods
= append(mods
, m
)
329 for _
, m
:= range mods
{
332 for _
, h
:= range list
{
333 fmt
.Fprintf(&buf
, "%s %s %s\n", m
.Path
, m
.Version
, h
)
337 data
, _
:= ioutil
.ReadFile(GoSumFile
)
338 if !bytes
.Equal(data
, buf
.Bytes()) {
339 if err
:= ioutil
.WriteFile(GoSumFile
, buf
.Bytes(), 0666); err
!= nil {
340 base
.Fatalf("go: writing go.sum: %v", err
)
344 if goSum
.modverify
!= "" {
345 os
.Remove(goSum
.modverify
)
349 // TrimGoSum trims go.sum to contain only the modules for which keep[m] is true.
350 func TrimGoSum(keep
map[module
.Version
]bool) {
352 defer goSum
.mu
.Unlock()
357 for m
:= range goSum
.m
{
358 // If we're keeping x@v we also keep x@v/go.mod.
359 // Map x@v/go.mod back to x@v for the keep lookup.
360 noGoMod
:= module
.Version
{Path
: m
.Path
, Version
: strings
.TrimSuffix(m
.Version
, "/go.mod")}
361 if !keep
[m
] && !keep
[noGoMod
] {