github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/pkg/tarsum/versioning.go (about) 1 package tarsum // import "github.com/Prakhar-Agarwal-byte/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 versionName, _, _ := strings.Cut(tarsum, "+") 73 version, ok := tarSumVersionsByName[versionName] 74 if !ok { 75 return -1, ErrNotVersion 76 } 77 return version, nil 78 } 79 80 // Errors that may be returned by functions in this package 81 var ( 82 ErrNotVersion = errors.New("string does not include a TarSum Version") 83 ErrVersionNotImplemented = errors.New("TarSum Version is not yet implemented") 84 ) 85 86 // tarHeaderSelector is the interface which different versions 87 // of tarsum should use for selecting and ordering tar headers 88 // for each item in the archive. 89 type tarHeaderSelector interface { 90 selectHeaders(h *tar.Header) (orderedHeaders [][2]string) 91 } 92 93 type tarHeaderSelectFunc func(h *tar.Header) (orderedHeaders [][2]string) 94 95 func (f tarHeaderSelectFunc) selectHeaders(h *tar.Header) (orderedHeaders [][2]string) { 96 return f(h) 97 } 98 99 func v0TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) { 100 return [][2]string{ 101 {"name", h.Name}, 102 {"mode", strconv.FormatInt(h.Mode, 10)}, 103 {"uid", strconv.Itoa(h.Uid)}, 104 {"gid", strconv.Itoa(h.Gid)}, 105 {"size", strconv.FormatInt(h.Size, 10)}, 106 {"mtime", strconv.FormatInt(h.ModTime.UTC().Unix(), 10)}, 107 {"typeflag", string([]byte{h.Typeflag})}, 108 {"linkname", h.Linkname}, 109 {"uname", h.Uname}, 110 {"gname", h.Gname}, 111 {"devmajor", strconv.FormatInt(h.Devmajor, 10)}, 112 {"devminor", strconv.FormatInt(h.Devminor, 10)}, 113 } 114 } 115 116 func v1TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) { 117 // Get extended attributes. 118 const paxSchilyXattr = "SCHILY.xattr." 119 var xattrs [][2]string 120 for k, v := range h.PAXRecords { 121 if xattr, ok := strings.CutPrefix(k, paxSchilyXattr); ok { 122 xattrs = append(xattrs, [2]string{xattr, v}) 123 } 124 } 125 sort.Slice(xattrs, func(i, j int) bool { return xattrs[i][0] < xattrs[j][0] }) 126 127 // Make the slice with enough capacity to hold the 11 basic headers 128 // we want from the v0 selector plus however many xattrs we have. 129 orderedHeaders = make([][2]string, 0, 11+len(xattrs)) 130 131 // Copy all headers from v0 excluding the 'mtime' header (the 5th element). 132 v0headers := v0TarHeaderSelect(h) 133 orderedHeaders = append(orderedHeaders, v0headers[0:5]...) 134 orderedHeaders = append(orderedHeaders, v0headers[6:]...) 135 136 // Finally, append the sorted xattrs. 137 orderedHeaders = append(orderedHeaders, xattrs...) 138 139 return 140 } 141 142 var registeredHeaderSelectors = map[Version]tarHeaderSelectFunc{ 143 Version0: v0TarHeaderSelect, 144 Version1: v1TarHeaderSelect, 145 VersionDev: v1TarHeaderSelect, 146 } 147 148 func getTarHeaderSelector(v Version) (tarHeaderSelector, error) { 149 headerSelector, ok := registeredHeaderSelectors[v] 150 if !ok { 151 return nil, ErrVersionNotImplemented 152 } 153 154 return headerSelector, nil 155 }