versioning.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package tarsum
  2. import (
  3. "errors"
  4. "sort"
  5. "strconv"
  6. "strings"
  7. "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
  8. )
  9. // versioning of the TarSum algorithm
  10. // based on the prefix of the hash used
  11. // i.e. "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"
  12. type Version int
  13. // Prefix of "tarsum"
  14. const (
  15. Version0 Version = iota
  16. Version1
  17. // NOTE: this variable will be either the latest or an unsettled next-version of the TarSum calculation
  18. VersionDev
  19. )
  20. // VersionLabelForChecksum returns the label for the given tarsum
  21. // checksum, i.e., everything before the first `+` character in
  22. // the string or an empty string if no label separator is found.
  23. func VersionLabelForChecksum(checksum string) string {
  24. // Checksums are in the form: {versionLabel}+{hashID}:{hex}
  25. sepIndex := strings.Index(checksum, "+")
  26. if sepIndex < 0 {
  27. return ""
  28. }
  29. return checksum[:sepIndex]
  30. }
  31. // Get a list of all known tarsum Version
  32. func GetVersions() []Version {
  33. v := []Version{}
  34. for k := range tarSumVersions {
  35. v = append(v, k)
  36. }
  37. return v
  38. }
  39. var (
  40. tarSumVersions = map[Version]string{
  41. Version0: "tarsum",
  42. Version1: "tarsum.v1",
  43. VersionDev: "tarsum.dev",
  44. }
  45. tarSumVersionsByName = map[string]Version{
  46. "tarsum": Version0,
  47. "tarsum.v1": Version1,
  48. "tarsum.dev": VersionDev,
  49. }
  50. )
  51. func (tsv Version) String() string {
  52. return tarSumVersions[tsv]
  53. }
  54. // GetVersionFromTarsum returns the Version from the provided string
  55. func GetVersionFromTarsum(tarsum string) (Version, error) {
  56. tsv := tarsum
  57. if strings.Contains(tarsum, "+") {
  58. tsv = strings.SplitN(tarsum, "+", 2)[0]
  59. }
  60. for v, s := range tarSumVersions {
  61. if s == tsv {
  62. return v, nil
  63. }
  64. }
  65. return -1, ErrNotVersion
  66. }
  67. // Errors that may be returned by functions in this package
  68. var (
  69. ErrNotVersion = errors.New("string does not include a TarSum Version")
  70. ErrVersionNotImplemented = errors.New("TarSum Version is not yet implemented")
  71. )
  72. // tarHeaderSelector is the interface which different versions
  73. // of tarsum should use for selecting and ordering tar headers
  74. // for each item in the archive.
  75. type tarHeaderSelector interface {
  76. selectHeaders(h *tar.Header) (orderedHeaders [][2]string)
  77. }
  78. type tarHeaderSelectFunc func(h *tar.Header) (orderedHeaders [][2]string)
  79. func (f tarHeaderSelectFunc) selectHeaders(h *tar.Header) (orderedHeaders [][2]string) {
  80. return f(h)
  81. }
  82. func v0TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) {
  83. return [][2]string{
  84. {"name", h.Name},
  85. {"mode", strconv.Itoa(int(h.Mode))},
  86. {"uid", strconv.Itoa(h.Uid)},
  87. {"gid", strconv.Itoa(h.Gid)},
  88. {"size", strconv.Itoa(int(h.Size))},
  89. {"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
  90. {"typeflag", string([]byte{h.Typeflag})},
  91. {"linkname", h.Linkname},
  92. {"uname", h.Uname},
  93. {"gname", h.Gname},
  94. {"devmajor", strconv.Itoa(int(h.Devmajor))},
  95. {"devminor", strconv.Itoa(int(h.Devminor))},
  96. }
  97. }
  98. func v1TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) {
  99. // Get extended attributes.
  100. xAttrKeys := make([]string, len(h.Xattrs))
  101. for k := range h.Xattrs {
  102. xAttrKeys = append(xAttrKeys, k)
  103. }
  104. sort.Strings(xAttrKeys)
  105. // Make the slice with enough capacity to hold the 11 basic headers
  106. // we want from the v0 selector plus however many xattrs we have.
  107. orderedHeaders = make([][2]string, 0, 11+len(xAttrKeys))
  108. // Copy all headers from v0 excluding the 'mtime' header (the 5th element).
  109. v0headers := v0TarHeaderSelect(h)
  110. orderedHeaders = append(orderedHeaders, v0headers[0:5]...)
  111. orderedHeaders = append(orderedHeaders, v0headers[6:]...)
  112. // Finally, append the sorted xattrs.
  113. for _, k := range xAttrKeys {
  114. orderedHeaders = append(orderedHeaders, [2]string{k, h.Xattrs[k]})
  115. }
  116. return
  117. }
  118. var registeredHeaderSelectors = map[Version]tarHeaderSelectFunc{
  119. Version0: v0TarHeaderSelect,
  120. Version1: v1TarHeaderSelect,
  121. VersionDev: v1TarHeaderSelect,
  122. }
  123. func getTarHeaderSelector(v Version) (tarHeaderSelector, error) {
  124. headerSelector, ok := registeredHeaderSelectors[v]
  125. if !ok {
  126. return nil, ErrVersionNotImplemented
  127. }
  128. return headerSelector, nil
  129. }