github.com/christopherobin/docker@v1.6.2/pkg/tarsum/versioning.go (about)

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