tarsum.go 3.8 KB

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