Files
nexarch/archive/internal/scrub/checksum.go
T
sysops 67bcdd1833 feat(archive): BAK-08 Checksum-basierte Objekt-Integritaetspruefung
Stichprobenbasierter Scrub-Job: nimmt BAK-05s existing_in_storage,
priorisiert nach eigenem scrub_state.last_scrubbed_at (nicht
file_revisions.created_at, sonst kein echtes Rotationsverhalten),
prueft Inhalt per SHA-256 gegen file_revisions.checksum_sha256.
Meldung ueber echten dauerhaften /metrics-Endpunkt (Pull-Modell,
OPS-03 scrapt, kein Push), Counter monoton steigend. Real registriert
in Core metrics_sources, End-zu-Ende ueber OPS-03-Aggregator bestaetigt,
realer Befund-Durchlauf mit absichtlich falscher Pruefsumme durchgefuehrt.
2026-08-29 23:55:24 +02:00

58 lines
1.7 KiB
Go

package scrub
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"github.com/jackc/pgx/v5/pgxpool"
)
// ExpectedChecksums liest file_revisions.checksum_sha256 fuer genau die
// uebergebenen storage_keys — bewusst eine eigene, minimale Abfrage statt
// Erweiterung von reconcile.DBEntry (BAK-05 bleibt existenz-only, keine
// Kopplung an Inhaltspruefungs-Bedarf von BAK-08).
func ExpectedChecksums(ctx context.Context, pool *pgxpool.Pool, storageKeys []string) (map[string]string, error) {
if len(storageKeys) == 0 {
return map[string]string{}, nil
}
rows, err := pool.Query(ctx, `
SELECT storage_key, checksum_sha256 FROM file_revisions WHERE storage_key = ANY($1)
`, storageKeys)
if err != nil {
return nil, fmt.Errorf("scrub: erwartete pruefsummen lesen: %w", err)
}
defer rows.Close()
out := make(map[string]string, len(storageKeys))
for rows.Next() {
var key, checksum string
if err := rows.Scan(&key, &checksum); err != nil {
return nil, fmt.Errorf("scrub: pruefsummen-zeile lesen: %w", err)
}
out[key] = checksum
}
return out, rows.Err()
}
// ActualChecksum liest die Datei unter baseDir/storageKey vollstaendig
// und berechnet ihren SHA-256 — echte Inhaltspruefung, kein
// Header-/Groessenvergleich (dieselbe Disziplin wie BAK-01s Verify).
func ActualChecksum(baseDir, storageKey string) (string, error) {
f, err := os.Open(filepath.Join(baseDir, filepath.FromSlash(storageKey)))
if err != nil {
return "", fmt.Errorf("scrub: objekt lesen: %w", err)
}
defer func() { _ = f.Close() }()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", fmt.Errorf("scrub: objekt hashen: %w", err)
}
return hex.EncodeToString(h.Sum(nil)), nil
}