hash.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Copyright 2017 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. package cache
  5. import (
  6. "bytes"
  7. "crypto/sha256"
  8. "fmt"
  9. "hash"
  10. "io"
  11. "os"
  12. "sync"
  13. )
  14. var debugHash = false // set when GODEBUG=gocachehash=1
  15. // HashSize is the number of bytes in a hash.
  16. const HashSize = 32
  17. // A Hash provides access to the canonical hash function used to index the cache.
  18. // The current implementation uses salted SHA256, but clients must not assume this.
  19. type Hash struct {
  20. h hash.Hash
  21. name string // for debugging
  22. buf *bytes.Buffer // for verify
  23. }
  24. // Subkey returns an action ID corresponding to mixing a parent
  25. // action ID with a string description of the subkey.
  26. func Subkey(parent ActionID, desc string) ActionID {
  27. h := sha256.New()
  28. h.Write([]byte("subkey:"))
  29. h.Write(parent[:])
  30. h.Write([]byte(desc))
  31. var out ActionID
  32. h.Sum(out[:0])
  33. if debugHash {
  34. fmt.Fprintf(os.Stderr, "HASH subkey %x %q = %x\n", parent, desc, out)
  35. }
  36. if verify {
  37. hashDebug.Lock()
  38. hashDebug.m[out] = fmt.Sprintf("subkey %x %q", parent, desc)
  39. hashDebug.Unlock()
  40. }
  41. return out
  42. }
  43. // NewHash returns a new Hash.
  44. // The caller is expected to Write data to it and then call Sum.
  45. func (c *Cache) NewHash(name string) *Hash {
  46. h := &Hash{h: sha256.New(), name: name}
  47. if debugHash {
  48. fmt.Fprintf(os.Stderr, "HASH[%s]\n", h.name)
  49. }
  50. h.Write(c.salt)
  51. if verify {
  52. h.buf = new(bytes.Buffer)
  53. }
  54. return h
  55. }
  56. // Write writes data to the running hash.
  57. func (h *Hash) Write(b []byte) (int, error) {
  58. if debugHash {
  59. fmt.Fprintf(os.Stderr, "HASH[%s]: %q\n", h.name, b)
  60. }
  61. if h.buf != nil {
  62. h.buf.Write(b)
  63. }
  64. return h.h.Write(b)
  65. }
  66. // Sum returns the hash of the data written previously.
  67. func (h *Hash) Sum() [HashSize]byte {
  68. var out [HashSize]byte
  69. h.h.Sum(out[:0])
  70. if debugHash {
  71. fmt.Fprintf(os.Stderr, "HASH[%s]: %x\n", h.name, out)
  72. }
  73. if h.buf != nil {
  74. hashDebug.Lock()
  75. if hashDebug.m == nil {
  76. hashDebug.m = make(map[[HashSize]byte]string)
  77. }
  78. hashDebug.m[out] = h.buf.String()
  79. hashDebug.Unlock()
  80. }
  81. return out
  82. }
  83. // In GODEBUG=gocacheverify=1 mode,
  84. // hashDebug holds the input to every computed hash ID,
  85. // so that we can work backward from the ID involved in a
  86. // cache entry mismatch to a description of what should be there.
  87. var hashDebug struct {
  88. sync.Mutex
  89. m map[[HashSize]byte]string
  90. }
  91. // reverseHash returns the input used to compute the hash id.
  92. func reverseHash(id [HashSize]byte) string {
  93. hashDebug.Lock()
  94. s := hashDebug.m[id]
  95. hashDebug.Unlock()
  96. return s
  97. }
  98. var hashFileCache struct {
  99. sync.Mutex
  100. m map[string][HashSize]byte
  101. }
  102. // FileHash returns the hash of the named file.
  103. // It caches repeated lookups for a given file,
  104. // and the cache entry for a file can be initialized
  105. // using SetFileHash.
  106. // The hash used by FileHash is not the same as
  107. // the hash used by NewHash.
  108. func FileHash(file string) ([HashSize]byte, error) {
  109. hashFileCache.Lock()
  110. out, ok := hashFileCache.m[file]
  111. hashFileCache.Unlock()
  112. if ok {
  113. return out, nil
  114. }
  115. h := sha256.New()
  116. f, err := os.Open(file)
  117. if err != nil {
  118. if debugHash {
  119. fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err)
  120. }
  121. return [HashSize]byte{}, err
  122. }
  123. _, err = io.Copy(h, f)
  124. f.Close()
  125. if err != nil {
  126. if debugHash {
  127. fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err)
  128. }
  129. return [HashSize]byte{}, err
  130. }
  131. h.Sum(out[:0])
  132. if debugHash {
  133. fmt.Fprintf(os.Stderr, "HASH %s: %x\n", file, out)
  134. }
  135. SetFileHash(file, out)
  136. return out, nil
  137. }
  138. // SetFileHash sets the hash returned by FileHash for file.
  139. func SetFileHash(file string, sum [HashSize]byte) {
  140. hashFileCache.Lock()
  141. if hashFileCache.m == nil {
  142. hashFileCache.m = make(map[string][HashSize]byte)
  143. }
  144. hashFileCache.m[file] = sum
  145. hashFileCache.Unlock()
  146. }