diff --git a/utils/checksum.go b/utils/checksum.go index 130c9420..3d003035 100644 --- a/utils/checksum.go +++ b/utils/checksum.go @@ -20,41 +20,73 @@ type ChecksumInfo struct { // ChecksumsForFile generates size, MD5, SHA1 & SHA256 checksums for given file func ChecksumsForFile(path string) (*ChecksumInfo, error) { - result := &ChecksumInfo{} - - st, err := os.Stat(path) - if err != nil { - return nil, err - } - - result.Size = st.Size() - - hashes := []hash.Hash{md5.New(), sha1.New(), sha256.New()} - file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() - buf := make([]byte, 8192) - for { - n, err := file.Read(buf) - if err == io.EOF { - break - } - if err != nil { - return nil, err - } + w := NewChecksumWriter(&DevNull{}) - for _, h := range hashes { - h.Write(buf[:n]) - } + _, err = io.Copy(w, file) + if err != nil { + return nil, err } - result.MD5 = fmt.Sprintf("%x", hashes[0].Sum(nil)) - result.SHA1 = fmt.Sprintf("%x", hashes[1].Sum(nil)) - result.SHA256 = fmt.Sprintf("%x", hashes[2].Sum(nil)) - - return result, nil + return w.Sum(), nil +} + +// DevNull just accepts anything +type DevNull struct { +} + +// Write to DevNull always succeeds +func (d *DevNull) Write(p []byte) (n int, err error) { + return len(p), nil +} + +// ChecksumWriter is a writer that does checksum calculation on the fly passing data +// to real writer +type ChecksumWriter struct { + w io.Writer + sum ChecksumInfo + hashes []hash.Hash +} + +// Interface check +var ( + _ io.Writer = &ChecksumWriter{} +) + +// NewChecksumWriter creates checksum calculator for given writer w +func NewChecksumWriter(w io.Writer) *ChecksumWriter { + return &ChecksumWriter{ + w: w, + hashes: []hash.Hash{md5.New(), sha1.New(), sha256.New()}, + } +} + +// Write implememnts pass-through writing with checksum calculation on the fly +func (c *ChecksumWriter) Write(p []byte) (n int, err error) { + n, err = c.w.Write(p) + if err != nil { + return n, err + } + + c.sum.Size += int64(n) + + for _, h := range c.hashes { + h.Write(p[:n]) + } + + return +} + +// Sum returns caculated ChecksumInfo +func (c *ChecksumWriter) Sum() *ChecksumInfo { + c.sum.MD5 = fmt.Sprintf("%x", c.hashes[0].Sum(nil)) + c.sum.SHA1 = fmt.Sprintf("%x", c.hashes[1].Sum(nil)) + c.sum.SHA256 = fmt.Sprintf("%x", c.hashes[2].Sum(nil)) + + return &c.sum } diff --git a/utils/checksum_test.go b/utils/checksum_test.go index 758ce9d6..5b1cfd5c 100644 --- a/utils/checksum_test.go +++ b/utils/checksum_test.go @@ -21,7 +21,7 @@ func (s *ChecksumSuite) TearDownTest(c *C) { s.tempfile.Close() } -func (s *ChecksumSuite) TestChecksum(c *C) { +func (s *ChecksumSuite) TestChecksumsForFile(c *C) { info, err := ChecksumsForFile(s.tempfile.Name()) c.Assert(err, IsNil)