fix(blob_consistency): raise a finding if decipher fails
This commit is contained in:
@@ -251,6 +251,12 @@ impl RecoverableJobHandler for BlobsConsistencyCheck {
|
||||
);
|
||||
}
|
||||
|
||||
// Snapshot "is this a Fresh run?" BEFORE the resume_cursor
|
||||
// match consumes it — otherwise the `is_none()` check later
|
||||
// borrows a partially-moved value. Fresh = no cursor bytes
|
||||
// at all; Resumed = cursor bytes present (possibly empty).
|
||||
let is_fresh = resume_cursor.is_none();
|
||||
|
||||
// Cursor = the last-visited `hash` string, UTF-8-encoded. On
|
||||
// resume, we walk `WHERE hash > $cursor` in ASC order. First
|
||||
// batch: NULL cursor → start from the smallest hash.
|
||||
@@ -272,10 +278,49 @@ impl RecoverableJobHandler for BlobsConsistencyCheck {
|
||||
// `record_finding` on each emission).
|
||||
let mut finding_count = 0u64;
|
||||
|
||||
// Deep mode = re-hash bytes for bit-rot detection. Logged
|
||||
// once at run start so operators tailing tracing know why the
|
||||
// scan is taking hours.
|
||||
if args.deep {
|
||||
// Deep mode is a per-run flag with two consumers:
|
||||
// 1. This handler — decides whether to re-hash bytes.
|
||||
// 2. The admin panel — needs to display whether the run
|
||||
// was deep so operators know what the scan actually
|
||||
// verified.
|
||||
//
|
||||
// On a Fresh run we take it from `deep` (the trigger
|
||||
// endpoint stamps `?deep=true` onto the args) and stash it
|
||||
// in `params.deep` so:
|
||||
// * Resume picks up the same mode (would previously become
|
||||
// non-deep on Resume — a Paused deep scan silently lost
|
||||
// its `deep` intent).
|
||||
// * The admin panel run-detail view can render
|
||||
// `params.deep = "true"` alongside `target_name`,
|
||||
// `progress_kind`, etc.
|
||||
//
|
||||
// Persist BEFORE the walk so a mid-fresh-batch crash still
|
||||
// leaves a Paused row with the right mode marker.
|
||||
let deep = if is_fresh {
|
||||
let deep = args.deep;
|
||||
let v = if deep { "true" } else { "false" };
|
||||
if let Err(e) = store.set_string_param("deep", v).await {
|
||||
return RunOutcome::Failed {
|
||||
message: format!("failed to persist deep flag to params: {e}"),
|
||||
};
|
||||
}
|
||||
deep
|
||||
} else {
|
||||
// Resumed run — read the persisted flag. Default to
|
||||
// false (fast mode) if the row is a pre-K3.5 Paused
|
||||
// scan without the param stashed.
|
||||
match store.get_string_param("deep").await {
|
||||
Ok(Some(v)) => v == "true",
|
||||
Ok(None) => false,
|
||||
Err(e) => {
|
||||
return RunOutcome::Failed {
|
||||
message: format!("read `deep` from params: {e}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if deep {
|
||||
tracing::info!(
|
||||
target: "oxicloud::consistency",
|
||||
event = "blobs_consistency.deep_mode_active",
|
||||
@@ -377,7 +422,7 @@ impl RecoverableJobHandler for BlobsConsistencyCheck {
|
||||
event = "blobs_consistency.completed",
|
||||
run_id = %store.run_id(),
|
||||
finding_count = finding_count,
|
||||
deep = args.deep,
|
||||
deep = deep,
|
||||
"blobs_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
@@ -472,7 +517,7 @@ impl RecoverableJobHandler for BlobsConsistencyCheck {
|
||||
// `expected_hash` was NOT reused as a name to
|
||||
// avoid mistaking it for "the hash we expect to
|
||||
// see on disk (i.e. what will fix this)".
|
||||
if args.deep {
|
||||
if deep {
|
||||
match recompute_hash(backend.as_ref(), &row.hash).await {
|
||||
Ok(computed_hash) if computed_hash == row.hash => {}
|
||||
Ok(computed_hash) => {
|
||||
@@ -524,7 +569,7 @@ impl RecoverableJobHandler for BlobsConsistencyCheck {
|
||||
event = "blobs_consistency.completed",
|
||||
run_id = %store.run_id(),
|
||||
finding_count = finding_count,
|
||||
deep = args.deep,
|
||||
deep = deep,
|
||||
"blobs_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user