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