github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/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  }