mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			158 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package tarsum
 | 
						|
 | 
						|
import (
 | 
						|
	"archive/tar"
 | 
						|
	"errors"
 | 
						|
	"io"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// Version is used for versioning of the TarSum algorithm
 | 
						|
// based on the prefix of the hash used
 | 
						|
// i.e. "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"
 | 
						|
type Version int
 | 
						|
 | 
						|
// Prefix of "tarsum"
 | 
						|
const (
 | 
						|
	Version0 Version = iota
 | 
						|
	Version1
 | 
						|
	// VersionDev this constant will be either the latest or an unsettled next-version of the TarSum calculation
 | 
						|
	VersionDev
 | 
						|
)
 | 
						|
 | 
						|
// WriteV1Header writes a tar header to a writer in V1 tarsum format.
 | 
						|
func WriteV1Header(h *tar.Header, w io.Writer) {
 | 
						|
	for _, elem := range v1TarHeaderSelect(h) {
 | 
						|
		w.Write([]byte(elem[0] + elem[1]))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// VersionLabelForChecksum returns the label for the given tarsum
 | 
						|
// checksum, i.e., everything before the first `+` character in
 | 
						|
// the string or an empty string if no label separator is found.
 | 
						|
func VersionLabelForChecksum(checksum string) string {
 | 
						|
	// Checksums are in the form: {versionLabel}+{hashID}:{hex}
 | 
						|
	sepIndex := strings.Index(checksum, "+")
 | 
						|
	if sepIndex < 0 {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	return checksum[:sepIndex]
 | 
						|
}
 | 
						|
 | 
						|
// GetVersions gets a list of all known tarsum versions.
 | 
						|
func GetVersions() []Version {
 | 
						|
	v := []Version{}
 | 
						|
	for k := range tarSumVersions {
 | 
						|
		v = append(v, k)
 | 
						|
	}
 | 
						|
	return v
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	tarSumVersions = map[Version]string{
 | 
						|
		Version0:   "tarsum",
 | 
						|
		Version1:   "tarsum.v1",
 | 
						|
		VersionDev: "tarsum.dev",
 | 
						|
	}
 | 
						|
	tarSumVersionsByName = map[string]Version{
 | 
						|
		"tarsum":     Version0,
 | 
						|
		"tarsum.v1":  Version1,
 | 
						|
		"tarsum.dev": VersionDev,
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
func (tsv Version) String() string {
 | 
						|
	return tarSumVersions[tsv]
 | 
						|
}
 | 
						|
 | 
						|
// GetVersionFromTarsum returns the Version from the provided string.
 | 
						|
func GetVersionFromTarsum(tarsum string) (Version, error) {
 | 
						|
	tsv := tarsum
 | 
						|
	if strings.Contains(tarsum, "+") {
 | 
						|
		tsv = strings.SplitN(tarsum, "+", 2)[0]
 | 
						|
	}
 | 
						|
	for v, s := range tarSumVersions {
 | 
						|
		if s == tsv {
 | 
						|
			return v, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return -1, ErrNotVersion
 | 
						|
}
 | 
						|
 | 
						|
// Errors that may be returned by functions in this package
 | 
						|
var (
 | 
						|
	ErrNotVersion            = errors.New("string does not include a TarSum Version")
 | 
						|
	ErrVersionNotImplemented = errors.New("TarSum Version is not yet implemented")
 | 
						|
)
 | 
						|
 | 
						|
// tarHeaderSelector is the interface which different versions
 | 
						|
// of tarsum should use for selecting and ordering tar headers
 | 
						|
// for each item in the archive.
 | 
						|
type tarHeaderSelector interface {
 | 
						|
	selectHeaders(h *tar.Header) (orderedHeaders [][2]string)
 | 
						|
}
 | 
						|
 | 
						|
type tarHeaderSelectFunc func(h *tar.Header) (orderedHeaders [][2]string)
 | 
						|
 | 
						|
func (f tarHeaderSelectFunc) selectHeaders(h *tar.Header) (orderedHeaders [][2]string) {
 | 
						|
	return f(h)
 | 
						|
}
 | 
						|
 | 
						|
func v0TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) {
 | 
						|
	return [][2]string{
 | 
						|
		{"name", h.Name},
 | 
						|
		{"mode", strconv.FormatInt(h.Mode, 10)},
 | 
						|
		{"uid", strconv.Itoa(h.Uid)},
 | 
						|
		{"gid", strconv.Itoa(h.Gid)},
 | 
						|
		{"size", strconv.FormatInt(h.Size, 10)},
 | 
						|
		{"mtime", strconv.FormatInt(h.ModTime.UTC().Unix(), 10)},
 | 
						|
		{"typeflag", string([]byte{h.Typeflag})},
 | 
						|
		{"linkname", h.Linkname},
 | 
						|
		{"uname", h.Uname},
 | 
						|
		{"gname", h.Gname},
 | 
						|
		{"devmajor", strconv.FormatInt(h.Devmajor, 10)},
 | 
						|
		{"devminor", strconv.FormatInt(h.Devminor, 10)},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func v1TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) {
 | 
						|
	// Get extended attributes.
 | 
						|
	xAttrKeys := make([]string, len(h.Xattrs))
 | 
						|
	for k := range h.Xattrs {
 | 
						|
		xAttrKeys = append(xAttrKeys, k)
 | 
						|
	}
 | 
						|
	sort.Strings(xAttrKeys)
 | 
						|
 | 
						|
	// Make the slice with enough capacity to hold the 11 basic headers
 | 
						|
	// we want from the v0 selector plus however many xattrs we have.
 | 
						|
	orderedHeaders = make([][2]string, 0, 11+len(xAttrKeys))
 | 
						|
 | 
						|
	// Copy all headers from v0 excluding the 'mtime' header (the 5th element).
 | 
						|
	v0headers := v0TarHeaderSelect(h)
 | 
						|
	orderedHeaders = append(orderedHeaders, v0headers[0:5]...)
 | 
						|
	orderedHeaders = append(orderedHeaders, v0headers[6:]...)
 | 
						|
 | 
						|
	// Finally, append the sorted xattrs.
 | 
						|
	for _, k := range xAttrKeys {
 | 
						|
		orderedHeaders = append(orderedHeaders, [2]string{k, h.Xattrs[k]})
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
var registeredHeaderSelectors = map[Version]tarHeaderSelectFunc{
 | 
						|
	Version0:   v0TarHeaderSelect,
 | 
						|
	Version1:   v1TarHeaderSelect,
 | 
						|
	VersionDev: v1TarHeaderSelect,
 | 
						|
}
 | 
						|
 | 
						|
func getTarHeaderSelector(v Version) (tarHeaderSelector, error) {
 | 
						|
	headerSelector, ok := registeredHeaderSelectors[v]
 | 
						|
	if !ok {
 | 
						|
		return nil, ErrVersionNotImplemented
 | 
						|
	}
 | 
						|
 | 
						|
	return headerSelector, nil
 | 
						|
}
 |