tarsum.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package tarsum
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "crypto/sha256"
  6. "encoding/hex"
  7. "hash"
  8. "io"
  9. "sort"
  10. "strconv"
  11. "strings"
  12. "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
  13. "github.com/docker/docker/pkg/log"
  14. )
  15. const (
  16. buf8K = 8 * 1024
  17. buf16K = 16 * 1024
  18. buf32K = 32 * 1024
  19. )
  20. type TarSum struct {
  21. io.Reader
  22. tarR *tar.Reader
  23. tarW *tar.Writer
  24. gz writeCloseFlusher
  25. bufTar *bytes.Buffer
  26. bufGz *bytes.Buffer
  27. bufData []byte
  28. h hash.Hash
  29. sums map[string]string
  30. currentFile string
  31. finished bool
  32. first bool
  33. DisableCompression bool
  34. }
  35. type writeCloseFlusher interface {
  36. io.WriteCloser
  37. Flush() error
  38. }
  39. type nopCloseFlusher struct {
  40. io.Writer
  41. }
  42. func (n *nopCloseFlusher) Close() error {
  43. return nil
  44. }
  45. func (n *nopCloseFlusher) Flush() error {
  46. return nil
  47. }
  48. func (ts *TarSum) encodeHeader(h *tar.Header) error {
  49. for _, elem := range [][2]string{
  50. {"name", h.Name},
  51. {"mode", strconv.Itoa(int(h.Mode))},
  52. {"uid", strconv.Itoa(h.Uid)},
  53. {"gid", strconv.Itoa(h.Gid)},
  54. {"size", strconv.Itoa(int(h.Size))},
  55. {"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
  56. {"typeflag", string([]byte{h.Typeflag})},
  57. {"linkname", h.Linkname},
  58. {"uname", h.Uname},
  59. {"gname", h.Gname},
  60. {"devmajor", strconv.Itoa(int(h.Devmajor))},
  61. {"devminor", strconv.Itoa(int(h.Devminor))},
  62. // {"atime", strconv.Itoa(int(h.AccessTime.UTC().Unix()))},
  63. // {"ctime", strconv.Itoa(int(h.ChangeTime.UTC().Unix()))},
  64. } {
  65. if _, err := ts.h.Write([]byte(elem[0] + elem[1])); err != nil {
  66. return err
  67. }
  68. }
  69. return nil
  70. }
  71. func (ts *TarSum) Read(buf []byte) (int, error) {
  72. if ts.gz == nil {
  73. ts.bufTar = bytes.NewBuffer([]byte{})
  74. ts.bufGz = bytes.NewBuffer([]byte{})
  75. ts.tarR = tar.NewReader(ts.Reader)
  76. ts.tarW = tar.NewWriter(ts.bufTar)
  77. if !ts.DisableCompression {
  78. ts.gz = gzip.NewWriter(ts.bufGz)
  79. } else {
  80. ts.gz = &nopCloseFlusher{Writer: ts.bufGz}
  81. }
  82. ts.h = sha256.New()
  83. ts.h.Reset()
  84. ts.first = true
  85. ts.sums = make(map[string]string)
  86. }
  87. if ts.finished {
  88. return ts.bufGz.Read(buf)
  89. }
  90. if ts.bufData == nil {
  91. switch {
  92. case len(buf) <= buf8K:
  93. ts.bufData = make([]byte, buf8K)
  94. case len(buf) <= buf16K:
  95. ts.bufData = make([]byte, buf16K)
  96. case len(buf) <= buf32K:
  97. ts.bufData = make([]byte, buf32K)
  98. default:
  99. ts.bufData = make([]byte, len(buf))
  100. }
  101. }
  102. buf2 := ts.bufData[:len(buf)-1]
  103. n, err := ts.tarR.Read(buf2)
  104. if err != nil {
  105. if err == io.EOF {
  106. if _, err := ts.h.Write(buf2[:n]); err != nil {
  107. return 0, err
  108. }
  109. if !ts.first {
  110. ts.sums[ts.currentFile] = hex.EncodeToString(ts.h.Sum(nil))
  111. ts.h.Reset()
  112. } else {
  113. ts.first = false
  114. }
  115. currentHeader, err := ts.tarR.Next()
  116. if err != nil {
  117. if err == io.EOF {
  118. if err := ts.gz.Close(); err != nil {
  119. return 0, err
  120. }
  121. ts.finished = true
  122. return n, nil
  123. }
  124. return n, err
  125. }
  126. ts.currentFile = strings.TrimSuffix(strings.TrimPrefix(currentHeader.Name, "./"), "/")
  127. if err := ts.encodeHeader(currentHeader); err != nil {
  128. return 0, err
  129. }
  130. if err := ts.tarW.WriteHeader(currentHeader); err != nil {
  131. return 0, err
  132. }
  133. if _, err := ts.tarW.Write(buf2[:n]); err != nil {
  134. return 0, err
  135. }
  136. ts.tarW.Flush()
  137. if _, err := io.Copy(ts.gz, ts.bufTar); err != nil {
  138. return 0, err
  139. }
  140. ts.gz.Flush()
  141. return ts.bufGz.Read(buf)
  142. }
  143. return n, err
  144. }
  145. // Filling the hash buffer
  146. if _, err = ts.h.Write(buf2[:n]); err != nil {
  147. return 0, err
  148. }
  149. // Filling the tar writter
  150. if _, err = ts.tarW.Write(buf2[:n]); err != nil {
  151. return 0, err
  152. }
  153. ts.tarW.Flush()
  154. // Filling the gz writter
  155. if _, err = io.Copy(ts.gz, ts.bufTar); err != nil {
  156. return 0, err
  157. }
  158. ts.gz.Flush()
  159. return ts.bufGz.Read(buf)
  160. }
  161. func (ts *TarSum) Sum(extra []byte) string {
  162. var sums []string
  163. for _, sum := range ts.sums {
  164. sums = append(sums, sum)
  165. }
  166. sort.Strings(sums)
  167. h := sha256.New()
  168. if extra != nil {
  169. h.Write(extra)
  170. }
  171. for _, sum := range sums {
  172. log.Debugf("-->%s<--", sum)
  173. h.Write([]byte(sum))
  174. }
  175. checksum := "tarsum+sha256:" + hex.EncodeToString(h.Sum(nil))
  176. log.Debugf("checksum processed: %s", checksum)
  177. return checksum
  178. }
  179. func (ts *TarSum) GetSums() map[string]string {
  180. return ts.sums
  181. }