forked from TrueCloudLab/distribution
3cfe9aede5
The Digest type will be fairly central for blob and layer management. The type presented in this package provides a number of core features that should enable reliable use within the registry. This commit will be followed by others that convert the storage layer and webapp to use this type as the primary layer/blob CAS identifier.
131 lines
2.5 KiB
Go
131 lines
2.5 KiB
Go
package digest
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"hash"
|
|
"io"
|
|
"io/ioutil"
|
|
|
|
"github.com/docker/docker/pkg/tarsum"
|
|
)
|
|
|
|
type Verifier interface {
|
|
io.Writer
|
|
|
|
// Verified will return true if the content written to Verifier matches
|
|
// the digest.
|
|
Verified() bool
|
|
|
|
// Planned methods:
|
|
// Err() error
|
|
// Reset()
|
|
}
|
|
|
|
func DigestVerifier(d Digest) Verifier {
|
|
alg := d.Algorithm()
|
|
switch alg {
|
|
case "md5", "sha1", "sha256":
|
|
return hashVerifier{
|
|
hash: newHash(alg),
|
|
digest: d,
|
|
}
|
|
default:
|
|
// Assume we have a tarsum.
|
|
version, err := tarsum.GetVersionFromTarsum(string(d))
|
|
if err != nil {
|
|
panic(err) // Always assume valid tarsum at this point.
|
|
}
|
|
|
|
pr, pw := io.Pipe()
|
|
|
|
// TODO(stevvooe): We may actually want to ban the earlier versions of
|
|
// tarsum. That decision may not be the place of the verifier.
|
|
|
|
ts, err := tarsum.NewTarSum(pr, true, version)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// TODO(sday): Ick! A goroutine per digest verification? We'll have to
|
|
// get the tarsum library to export an io.Writer variant.
|
|
go func() {
|
|
io.Copy(ioutil.Discard, ts)
|
|
pw.Close()
|
|
}()
|
|
|
|
return &tarsumVerifier{
|
|
digest: d,
|
|
ts: ts,
|
|
pr: pr,
|
|
pw: pw,
|
|
}
|
|
}
|
|
|
|
panic("unsupported digest: " + d)
|
|
}
|
|
|
|
// LengthVerifier returns a verifier that returns true when the number of read
|
|
// bytes equals the expected parameter.
|
|
func LengthVerifier(expected int64) Verifier {
|
|
return &lengthVerifier{
|
|
expected: expected,
|
|
}
|
|
}
|
|
|
|
type lengthVerifier struct {
|
|
expected int64 // expected bytes read
|
|
len int64 // bytes read
|
|
}
|
|
|
|
func (lv *lengthVerifier) Write(p []byte) (n int, err error) {
|
|
n = len(p)
|
|
lv.len += int64(n)
|
|
return n, err
|
|
}
|
|
|
|
func (lv *lengthVerifier) Verified() bool {
|
|
return lv.expected == lv.len
|
|
}
|
|
|
|
func newHash(name string) hash.Hash {
|
|
switch name {
|
|
case "sha256":
|
|
return sha256.New()
|
|
case "sha1":
|
|
return sha1.New()
|
|
case "md5":
|
|
return md5.New()
|
|
default:
|
|
panic("unsupport algorithm: " + name)
|
|
}
|
|
}
|
|
|
|
type hashVerifier struct {
|
|
digest Digest
|
|
hash hash.Hash
|
|
}
|
|
|
|
func (hv hashVerifier) Write(p []byte) (n int, err error) {
|
|
return hv.hash.Write(p)
|
|
}
|
|
|
|
func (hv hashVerifier) Verified() bool {
|
|
return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash)
|
|
}
|
|
|
|
type tarsumVerifier struct {
|
|
digest Digest
|
|
ts tarsum.TarSum
|
|
pr *io.PipeReader
|
|
pw *io.PipeWriter
|
|
}
|
|
|
|
func (tv *tarsumVerifier) Write(p []byte) (n int, err error) {
|
|
return tv.pw.Write(p)
|
|
}
|
|
|
|
func (tv *tarsumVerifier) Verified() bool {
|
|
return tv.digest == Digest(tv.ts.Sum(nil))
|
|
}
|