github.com/bir3/gocompiler@v0.9.2202/src/xvendor/golang.org/x/mod/sumdb/dirhash/hash.go (about) 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 // sha256sum $(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 } else if file == dir { 94 return fmt.Errorf("%s is not a directory", dir) 95 } 96 97 rel := file 98 if dir != "." { 99 rel = file[len(dir)+1:] 100 } 101 f := filepath.Join(prefix, rel) 102 files = append(files, filepath.ToSlash(f)) 103 return nil 104 }) 105 if err != nil { 106 return nil, err 107 } 108 return files, nil 109 } 110 111 // HashZip returns the hash of the file content in the named zip file. 112 // Only the file names and their contents are included in the hash: 113 // the exact zip file format encoding, compression method, 114 // per-file modification times, and other metadata are ignored. 115 func HashZip(zipfile string, hash Hash) (string, error) { 116 z, err := zip.OpenReader(zipfile) 117 if err != nil { 118 return "", err 119 } 120 defer z.Close() 121 var files []string 122 zfiles := make(map[string]*zip.File) 123 for _, file := range z.File { 124 files = append(files, file.Name) 125 zfiles[file.Name] = file 126 } 127 zipOpen := func(name string) (io.ReadCloser, error) { 128 f := zfiles[name] 129 if f == nil { 130 return nil, fmt.Errorf("file %q not found in zip", name) // should never happen 131 } 132 return f.Open() 133 } 134 return hash(files, zipOpen) 135 }