123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- 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
- }
|