Merge branch 'master' into 7179-test-mocks
[arvados.git] / services / keepstore / collision.go
1 package main
2
3 import (
4         "crypto/md5"
5         "fmt"
6         "io"
7 )
8
9 // Compute the MD5 digest of a data block (consisting of buf1 + buf2 +
10 // all bytes readable from rdr). If all data is read successfully,
11 // return DiskHashError or CollisionError depending on whether it
12 // matches expectMD5. If an error occurs while reading, return that
13 // error.
14 //
15 // "content has expected MD5" is called a collision because this
16 // function is used in cases where we have another block in hand with
17 // the given MD5 but different content.
18 func collisionOrCorrupt(expectMD5 string, buf1, buf2 []byte, rdr io.Reader) error {
19         outcome := make(chan error)
20         data := make(chan []byte, 1)
21         go func() {
22                 h := md5.New()
23                 for b := range data {
24                         h.Write(b)
25                 }
26                 if fmt.Sprintf("%x", h.Sum(nil)) == expectMD5 {
27                         outcome <- CollisionError
28                 } else {
29                         outcome <- DiskHashError
30                 }
31         }()
32         data <- buf1
33         if buf2 != nil {
34                 data <- buf2
35         }
36         var err error
37         for rdr != nil && err == nil {
38                 buf := make([]byte, 1 << 18)
39                 var n int
40                 n, err = rdr.Read(buf)
41                 data <- buf[:n]
42         }
43         close(data)
44         if rdr != nil && err != io.EOF {
45                 <-outcome
46                 return err
47         }
48         return <-outcome
49 }