tasks 1-3
This commit is contained in:
+20
-1
@@ -2,14 +2,19 @@ package erf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var expectedSHA256Pattern = regexp.MustCompile(`^[0-9a-f]{64}$`)
|
||||
|
||||
const (
|
||||
headerSize = 160
|
||||
versionV10 = "V1.0"
|
||||
@@ -30,6 +35,9 @@ type Resource struct {
|
||||
Data []byte
|
||||
SourcePath string
|
||||
Size int64
|
||||
// ExpectedSHA256, when set, is the lowercase hex SHA-256 the streamed
|
||||
// SourcePath payload must hash to. A mismatch fails the write.
|
||||
ExpectedSHA256 string
|
||||
}
|
||||
|
||||
type header struct {
|
||||
@@ -407,18 +415,29 @@ func writeResourceData(w io.Writer, resource Resource) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if resource.ExpectedSHA256 != "" && !expectedSHA256Pattern.MatchString(resource.ExpectedSHA256) {
|
||||
return fmt.Errorf("resource %q has invalid expected sha256", resource.SourcePath)
|
||||
}
|
||||
|
||||
file, err := os.Open(resource.SourcePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open resource %q: %w", resource.SourcePath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
written, err := io.Copy(w, file)
|
||||
hash := sha256.New()
|
||||
written, err := io.Copy(io.MultiWriter(w, hash), file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy resource %q: %w", resource.SourcePath, err)
|
||||
}
|
||||
if resource.Size > 0 && written != resource.Size {
|
||||
return fmt.Errorf("copy resource %q: expected %d bytes, wrote %d", resource.SourcePath, resource.Size, written)
|
||||
}
|
||||
if resource.ExpectedSHA256 != "" {
|
||||
got := hex.EncodeToString(hash.Sum(nil))
|
||||
if got != resource.ExpectedSHA256 {
|
||||
return fmt.Errorf("resource %q sha256 mismatch: expected %s, got %s", resource.SourcePath, resource.ExpectedSHA256, got)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,9 +2,72 @@ package erf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func writeTempPayload(t *testing.T, payload []byte) string {
|
||||
t.Helper()
|
||||
path := filepath.Join(t.TempDir(), "payload.bin")
|
||||
if err := os.WriteFile(path, payload, 0o644); err != nil {
|
||||
t.Fatalf("write payload: %v", err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func TestWriteRejectsSourcePathSHA256Mismatch(t *testing.T) {
|
||||
payload := []byte("hello world")
|
||||
path := writeTempPayload(t, payload)
|
||||
|
||||
archive := New("HAK ", []Resource{{
|
||||
Name: "asset",
|
||||
Type: 0x0003,
|
||||
SourcePath: path,
|
||||
Size: int64(len(payload)),
|
||||
ExpectedSHA256: strings.Repeat("a", 64),
|
||||
}})
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := Write(&buf, archive)
|
||||
if err == nil {
|
||||
t.Fatal("expected sha256 mismatch error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "sha256 mismatch") {
|
||||
t.Fatalf("error = %v, want sha256 mismatch", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteAcceptsMatchingSourcePathSHA256(t *testing.T) {
|
||||
payload := []byte("hello world")
|
||||
path := writeTempPayload(t, payload)
|
||||
sum := sha256.Sum256(payload)
|
||||
|
||||
archive := New("HAK ", []Resource{{
|
||||
Name: "asset",
|
||||
Type: 0x0003,
|
||||
SourcePath: path,
|
||||
Size: int64(len(payload)),
|
||||
ExpectedSHA256: hex.EncodeToString(sum[:]),
|
||||
}})
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := Write(&buf, archive); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
decoded, err := Read(bytes.NewReader(buf.Bytes()))
|
||||
if err != nil {
|
||||
t.Fatalf("read: %v", err)
|
||||
}
|
||||
if len(decoded.Resources) != 1 || string(decoded.Resources[0].Data) != string(payload) {
|
||||
t.Fatalf("unexpected payload: %#v", decoded.Resources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchiveRoundTrip(t *testing.T) {
|
||||
archive := New("MOD ", []Resource{
|
||||
{Name: "module", Type: 0x07DE, Data: []byte("ifo")},
|
||||
|
||||
Reference in New Issue
Block a user