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