Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / mod@v0.3.0 / sumdb / dirhash / hash.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 dirhash defines hashes over directory trees.
6 // These hashes are recorded in go.sum files and in the Go checksum database,
7 // to allow verifying that a newly-downloaded module has the expected content.
8 package dirhash
9
10 import (
11         "archive/zip"
12         "crypto/sha256"
13         "encoding/base64"
14         "errors"
15         "fmt"
16         "io"
17         "os"
18         "path/filepath"
19         "sort"
20         "strings"
21 )
22
23 // DefaultHash is the default hash function used in new go.sum entries.
24 var DefaultHash Hash = Hash1
25
26 // A Hash is a directory hash function.
27 // It accepts a list of files along with a function that opens the content of each file.
28 // It opens, reads, hashes, and closes each file and returns the overall directory hash.
29 type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error)
30
31 // Hash1 is the "h1:" directory hash function, using SHA-256.
32 //
33 // Hash1 is "h1:" followed by the base64-encoded SHA-256 hash of a summary
34 // prepared as if by the Unix command:
35 //
36 //      find . -type f | sort | sha256sum
37 //
38 // More precisely, the hashed summary contains a single line for each file in the list,
39 // ordered by sort.Strings applied to the file names, where each line consists of
40 // the hexadecimal SHA-256 hash of the file content,
41 // two spaces (U+0020), the file name, and a newline (U+000A).
42 //
43 // File names with newlines (U+000A) are disallowed.
44 func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) {
45         h := sha256.New()
46         files = append([]string(nil), files...)
47         sort.Strings(files)
48         for _, file := range files {
49                 if strings.Contains(file, "\n") {
50                         return "", errors.New("dirhash: filenames with newlines are not supported")
51                 }
52                 r, err := open(file)
53                 if err != nil {
54                         return "", err
55                 }
56                 hf := sha256.New()
57                 _, err = io.Copy(hf, r)
58                 r.Close()
59                 if err != nil {
60                         return "", err
61                 }
62                 fmt.Fprintf(h, "%x  %s\n", hf.Sum(nil), file)
63         }
64         return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
65 }
66
67 // HashDir returns the hash of the local file system directory dir,
68 // replacing the directory name itself with prefix in the file names
69 // used in the hash function.
70 func HashDir(dir, prefix string, hash Hash) (string, error) {
71         files, err := DirFiles(dir, prefix)
72         if err != nil {
73                 return "", err
74         }
75         osOpen := func(name string) (io.ReadCloser, error) {
76                 return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix)))
77         }
78         return hash(files, osOpen)
79 }
80
81 // DirFiles returns the list of files in the tree rooted at dir,
82 // replacing the directory name dir with prefix in each name.
83 // The resulting names always use forward slashes.
84 func DirFiles(dir, prefix string) ([]string, error) {
85         var files []string
86         dir = filepath.Clean(dir)
87         err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
88                 if err != nil {
89                         return err
90                 }
91                 if info.IsDir() {
92                         return nil
93                 }
94                 rel := file
95                 if dir != "." {
96                         rel = file[len(dir)+1:]
97                 }
98                 f := filepath.Join(prefix, rel)
99                 files = append(files, filepath.ToSlash(f))
100                 return nil
101         })
102         if err != nil {
103                 return nil, err
104         }
105         return files, nil
106 }
107
108 // HashZip returns the hash of the file content in the named zip file.
109 // Only the file names and their contents are included in the hash:
110 // the exact zip file format encoding, compression method,
111 // per-file modification times, and other metadata are ignored.
112 func HashZip(zipfile string, hash Hash) (string, error) {
113         z, err := zip.OpenReader(zipfile)
114         if err != nil {
115                 return "", err
116         }
117         defer z.Close()
118         var files []string
119         zfiles := make(map[string]*zip.File)
120         for _, file := range z.File {
121                 files = append(files, file.Name)
122                 zfiles[file.Name] = file
123         }
124         zipOpen := func(name string) (io.ReadCloser, error) {
125                 f := zfiles[name]
126                 if f == nil {
127                         return nil, fmt.Errorf("file %q not found in zip", name) // should never happen
128                 }
129                 return f.Open()
130         }
131         return hash(files, zipOpen)
132 }