feat(bsr/checksum): Add function to load SHA256SUM
This provides a Sha256Sums type to store the information from a SHA256SUM file in memory for easy lookup of the sums of files. It can be used when checking the checksums of individual files without needing to go back to disk.
This commit is contained in:
2 files changed
+192
No files matched your search
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
sha256sumSep = " "
|
||||
sha256sumBinarySep = " *"
|
||||
sha256HexLength = 64
|
||||
)
|
||||
|
||||
// Sha256Sums is a map of file names with their corresponding SHA256SUM.
|
||||
type Sha256Sums map[string][]byte
|
||||
|
||||
// LoadSha256Sums reads from an io.Reader to populate the Sha256Sums map The
|
||||
// reader is expected to return the sums in a format that is compatible with
|
||||
// sha256sum, that is each line should contain the hex encoded sum followed by
|
||||
// a separator and then the file name. The separator should either be two
|
||||
// spaces (" ") or a space an asterisks (" *"). The former indicates a text
|
||||
// file and the later a binary file. However, there is no difference between
|
||||
// these for GNU systems. Therefore this also makes no distinction between the
|
||||
// separators and checks for either.
|
||||
//
|
||||
// See: https://man7.org/linux/man-pages/man1/sha256sum.1.html
|
||||
func LoadSha256Sums(r io.Reader) (Sha256Sums, error) {
|
||||
const op = "checksum.LoadSha256Sums"
|
||||
|
||||
s := make(Sha256Sums)
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
var sum, file string
|
||||
var ok bool
|
||||
|
||||
sum, file, ok = strings.Cut(scanner.Text(), sha256sumSep)
|
||||
if !ok {
|
||||
sum, file, ok = strings.Cut(scanner.Text(), sha256sumBinarySep)
|
||||
}
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: improperly formated line", op)
|
||||
}
|
||||
|
||||
if len(sum) != sha256HexLength {
|
||||
return nil, fmt.Errorf("%s: improperly formated line", op)
|
||||
}
|
||||
|
||||
if _, dup := s[file]; dup {
|
||||
return nil, fmt.Errorf("%s: duplicate file", op)
|
||||
}
|
||||
|
||||
s[file] = []byte(sum)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Sum returns the sum for the provided file, or an error if there is no sum.
|
||||
func (s Sha256Sums) Sum(f string) ([]byte, error) {
|
||||
const op = "checksum.(Sha256Sums).Sum"
|
||||
|
||||
sum, ok := s[f]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: no sum for file %s", op, f)
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package checksum_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/bsr/internal/checksum"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLoadSha256Sums(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
r io.Reader
|
||||
want checksum.Sha256Sums
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
"valid-multi-line-text",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e file1
|
||||
7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53 file2
|
||||
`),
|
||||
checksum.Sha256Sums{
|
||||
"file1": []byte("ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e"),
|
||||
"file2": []byte("7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53"),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"valid-multi-line-binary",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e *file1
|
||||
7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53 *file2
|
||||
`),
|
||||
checksum.Sha256Sums{
|
||||
"file1": []byte("ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e"),
|
||||
"file2": []byte("7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53"),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"valid-multi-line-text-and-binary",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e file1
|
||||
7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53 *file2
|
||||
`),
|
||||
checksum.Sha256Sums{
|
||||
"file1": []byte("ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e"),
|
||||
"file2": []byte("7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53"),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"valid-multi-line-binary-and-text",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e *file1
|
||||
7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53 file2
|
||||
`),
|
||||
checksum.Sha256Sums{
|
||||
"file1": []byte("ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e"),
|
||||
"file2": []byte("7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53"),
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"invalid-single-space",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e file1`),
|
||||
nil,
|
||||
fmt.Errorf("checksum.LoadSha256Sums: improperly formated line"),
|
||||
},
|
||||
{
|
||||
"invalid-no-file",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e`),
|
||||
nil,
|
||||
fmt.Errorf("checksum.LoadSha256Sums: improperly formated line"),
|
||||
},
|
||||
{
|
||||
"invalid-no-sum-file",
|
||||
strings.NewReader(`file1`),
|
||||
nil,
|
||||
fmt.Errorf("checksum.LoadSha256Sums: improperly formated line"),
|
||||
},
|
||||
{
|
||||
"invalid-hash-too-short",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00 file1`),
|
||||
nil,
|
||||
fmt.Errorf("checksum.LoadSha256Sums: improperly formated line"),
|
||||
},
|
||||
{
|
||||
"invalid-hash-too-long",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00ef file1`),
|
||||
nil,
|
||||
fmt.Errorf("checksum.LoadSha256Sums: improperly formated line"),
|
||||
},
|
||||
{
|
||||
"invalid-duplicate-file-name",
|
||||
strings.NewReader(`ffe65f1d98fafedea3514adc956c8ada5980c6c5d2552fd61f48401aefd5c00e file1
|
||||
7849ccae8fa91bb281a118041abf921636e68853059329ac68fccd8d518cea53 file1
|
||||
`),
|
||||
nil,
|
||||
fmt.Errorf("checksum.LoadSha256Sums: duplicate file"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := checksum.LoadSha256Sums(tc.r)
|
||||
if tc.wantErr != nil {
|
||||
require.EqualError(t, err, tc.wantErr.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user