Skip to main content

ic_backup/ops/persistence/consistency/
mod.rs

1//! Bounded immutable original-plan consistency requirement retention; no fence authority.
2
3use super::{
4    BackupLayoutGuard, JournalLockError, OperationPlanPersistenceError, PersistenceError,
5    immutable_record, read_operation_plan,
6};
7use crate::model::{
8    artifacts::ArtifactChecksumRecord,
9    consistency::{
10        ConsistencyRequirementError, ConsistencyRequirementRecord,
11        MAX_CONSISTENCY_REQUIREMENT_BYTES,
12    },
13    operation_plan::OperationPlanRecord,
14};
15use thiserror::Error;
16
17/// Durably create fixed `consistency-requirement.json` without replacing original evidence.
18///
19/// Requires the exact original plan already retained under layout exclusion. This
20/// persists the reviewed guarantee, not an active fence, paid allowance or release permit.
21/// # Errors
22/// Rejects mismatched/missing original plan, excessive bytes, unsafe/existing paths and IO/locks.
23pub fn create_consistency_requirement(
24    layout: &BackupLayoutGuard,
25    plan: &OperationPlanRecord,
26    record: &ConsistencyRequirementRecord,
27) -> Result<(), ConsistencyPersistenceError> {
28    record.validate_plan(plan)?;
29    read_operation_plan(layout, &plan.digest())?;
30    immutable_record::create(
31        layout,
32        "consistency-requirement.json",
33        MAX_CONSISTENCY_REQUIREMENT_BYTES,
34        record,
35    )
36}
37/// Read bounded validated original requirement under its exact expected digest and retained plan.
38///
39/// Lost local creation replies reconcile by this read; never overwrite/downgrade an
40/// existing guarantee or recover a fence/consumed authority from absence.
41/// # Errors
42/// Rejects unsafe/missing/oversized/invalid declarations, original plan or requirement mismatch.
43pub fn read_consistency_requirement(
44    layout: &BackupLayoutGuard,
45    plan: &OperationPlanRecord,
46    expected: &ArtifactChecksumRecord,
47) -> Result<ConsistencyRequirementRecord, ConsistencyPersistenceError> {
48    read_operation_plan(layout, &plan.digest())?;
49    immutable_record::read_with(
50        layout,
51        "consistency-requirement.json",
52        MAX_CONSISTENCY_REQUIREMENT_BYTES,
53        |record: &ConsistencyRequirementRecord| {
54            record.validate_plan(plan)?;
55            if &record.digest() != expected {
56                return Err(ConsistencyPersistenceError::DigestMismatch);
57            }
58            Ok(())
59        },
60    )
61}
62/// Typed exact retained requirement or bounded local storage denial.
63#[derive(Debug, Error)]
64pub enum ConsistencyPersistenceError {
65    /// Retained guarantee differs from the exact originally expected requirement.
66    #[error("consistency requirement digest mismatch")]
67    DigestMismatch,
68    /// Original full plan identity differs.
69    #[error(transparent)]
70    Requirement(#[from] ConsistencyRequirementError),
71    /// Original plan is not admitted from the held layout.
72    #[error(transparent)]
73    Plan(#[from] OperationPlanPersistenceError),
74    /// Layout/journal exclusion failed.
75    #[error(transparent)]
76    Lock(#[from] JournalLockError),
77    /// Bounded JSON or durable filesystem access failed.
78    #[error(transparent)]
79    Persistence(#[from] PersistenceError),
80}
81
82#[cfg(all(test, unix))]
83mod tests;