16b74247fb
Some structures use int for sizes and UNIX timestamps. On some platforms, int is 32 bits, so this can lead to the year 2038 issues and overflows when dealing with large containers or layers. Consistently use int64 to store sizes and UNIX timestamps in api/types/types.go. Update related to code accordingly (i.e. strconv.FormatInt instead of strconv.Itoa). Use int64 in progressreader package to avoid integer overflow when dealing with large quantities. Update related code accordingly. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
150 lines
4 KiB
Go
150 lines
4 KiB
Go
package tarsum
|
|
|
|
import (
|
|
"archive/tar"
|
|
"errors"
|
|
"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
|
|
)
|
|
|
|
// 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
|
|
}
|