ic-backup 0.24.2

Host-side snapshot backup and same-release recovery for Internet Computer canisters
Documentation
//! Immutable original restore/source safety retention under both layout guards.

use super::{
    BackupLayoutGuard, JournalLockError, OperationPlanPersistenceError, PersistenceError,
    immutable_record, read_operation_plan,
};
use crate::model::{
    artifacts::ArtifactChecksumRecord,
    operation_plan::OperationPlanRecord,
    restore_safety::{
        MAX_RESTORE_SAFETY_REQUIREMENT_BYTES, RestoreSafetyRequirementError,
        RestoreSafetyRequirementRecord,
    },
};
use thiserror::Error;

/// Durably create fixed `restore-safety-requirement.json` without replacement.
///
/// Both exact original plans must already be retained under their layout guards.
/// Artifact completeness, source-reference retention and current application/fence
/// custody remain separately admitted by their owners. This persists declarations.
/// # Errors
/// Rejects changed/missing plans, inappropriate source, excessive bytes or unsafe/existing paths.
pub fn create_restore_safety_requirement(
    layout: &BackupLayoutGuard,
    source_layout: &BackupLayoutGuard,
    plan: &OperationPlanRecord,
    source: &OperationPlanRecord,
    record: &RestoreSafetyRequirementRecord,
) -> Result<(), RestoreSafetyPersistenceError> {
    record.validate_plans(plan, source)?;
    read_operation_plan(layout, &plan.digest())?;
    read_operation_plan(source_layout, &source.digest())?;
    immutable_record::create(
        layout,
        "restore-safety-requirement.json",
        MAX_RESTORE_SAFETY_REQUIREMENT_BYTES,
        record,
    )
}
/// Read bounded exact original requirement under both unchanged retained plans.
///
/// Lost creation replies reconcile through this read; absence never recreates a
/// requirement, grants fresh source/fence authority or replenishes original attempts.
/// # Errors
/// Rejects missing/unsafe/oversized records, changed plans or exact requirement digest mismatch.
pub fn read_restore_safety_requirement(
    layout: &BackupLayoutGuard,
    source_layout: &BackupLayoutGuard,
    plan: &OperationPlanRecord,
    source: &OperationPlanRecord,
    expected: &ArtifactChecksumRecord,
) -> Result<RestoreSafetyRequirementRecord, RestoreSafetyPersistenceError> {
    read_operation_plan(layout, &plan.digest())?;
    read_operation_plan(source_layout, &source.digest())?;
    immutable_record::read_with(
        layout,
        "restore-safety-requirement.json",
        MAX_RESTORE_SAFETY_REQUIREMENT_BYTES,
        |record: &RestoreSafetyRequirementRecord| {
            record.validate_plans(plan, source)?;
            if &record.digest() != expected {
                return Err(RestoreSafetyPersistenceError::DigestMismatch);
            }
            Ok(())
        },
    )
}
/// Typed original restore/source safety persistence denial.
#[derive(Debug, Error)]
pub enum RestoreSafetyPersistenceError {
    /// Exact original retained requirement differs.
    #[error("restore safety requirement digest mismatch")]
    DigestMismatch,
    /// Original source or restore declarations differ.
    #[error(transparent)]
    Requirement(#[from] RestoreSafetyRequirementError),
    /// An exact original plan is not retained under its unchanged layout.
    #[error(transparent)]
    Plan(#[from] OperationPlanPersistenceError),
    /// Journal exclusion failed.
    #[error(transparent)]
    Lock(#[from] JournalLockError),
    /// Bounded JSON/durable IO failed.
    #[error(transparent)]
    Persistence(#[from] PersistenceError),
}

#[cfg(all(test, unix))]
mod tests;