digests.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package continuity
  14. import (
  15. "fmt"
  16. "io"
  17. "sort"
  18. "github.com/opencontainers/go-digest"
  19. )
  20. // Digester produces a digest for a given read stream
  21. type Digester interface {
  22. Digest(io.Reader) (digest.Digest, error)
  23. }
  24. // ContentProvider produces a read stream for a given digest
  25. type ContentProvider interface {
  26. Reader(digest.Digest) (io.ReadCloser, error)
  27. }
  28. type simpleDigester struct {
  29. algorithm digest.Algorithm
  30. }
  31. func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) {
  32. digester := sd.algorithm.Digester()
  33. if _, err := io.Copy(digester.Hash(), r); err != nil {
  34. return "", err
  35. }
  36. return digester.Digest(), nil
  37. }
  38. // uniqifyDigests sorts and uniqifies the provided digest, ensuring that the
  39. // digests are not repeated and no two digests with the same algorithm have
  40. // different values. Because a stable sort is used, this has the effect of
  41. // "zipping" digest collections from multiple resources.
  42. func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) {
  43. sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here.
  44. seen := map[digest.Digest]struct{}{}
  45. algs := map[digest.Algorithm][]digest.Digest{} // detect different digests.
  46. var out []digest.Digest
  47. // uniqify the digests
  48. for _, d := range digests {
  49. if _, ok := seen[d]; ok {
  50. continue
  51. }
  52. seen[d] = struct{}{}
  53. algs[d.Algorithm()] = append(algs[d.Algorithm()], d)
  54. if len(algs[d.Algorithm()]) > 1 {
  55. return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm())
  56. }
  57. out = append(out, d)
  58. }
  59. return out, nil
  60. }
  61. // digestsMatch compares the two sets of digests to see if they match.
  62. func digestsMatch(as, bs []digest.Digest) bool {
  63. all := append(as, bs...)
  64. uniqified, err := uniqifyDigests(all...)
  65. if err != nil {
  66. // the only error uniqifyDigests returns is when the digests disagree.
  67. return false
  68. }
  69. disjoint := len(as) + len(bs)
  70. if len(uniqified) == disjoint {
  71. // if these two sets have the same cardinality, we know both sides
  72. // didn't share any digests.
  73. return false
  74. }
  75. return true
  76. }
  77. type digestSlice []digest.Digest
  78. func (p digestSlice) Len() int { return len(p) }
  79. func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] }
  80. func (p digestSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }