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 }