Skip to main content

ic_backup/ops/persistence/
mod.rs

1//! Durable publication, journal/layout exclusion and retained local dependencies.
2
3mod artifact_commit;
4mod attempt_journal;
5mod command_lifetime_lock;
6mod download_journal;
7mod execution_progress;
8mod execution_settlement;
9mod execution_workflow;
10mod immutable_record;
11mod journal_lock;
12mod journal_path;
13pub(crate) mod json;
14mod layout_lifetime;
15mod operation_plan;
16mod restore_reference_release;
17mod restore_safety;
18
19pub use artifact_commit::{ArtifactCommitOutcome, commit_artifact_directory};
20pub use attempt_journal::{AttemptJournalError, AttemptJournalGuard};
21pub use command_lifetime_lock::{
22    COMMAND_CUSTODY_DESCRIPTOR_ENV, CommandLifetimeLock, CommandLifetimeLockError,
23    CommandQuiescenceGuard,
24};
25pub use download_journal::{
26    DownloadIntegrityError, DownloadJournalError, DownloadJournalGuard, DownloadManifestError,
27    read_download_manifest,
28};
29#[cfg(unix)]
30pub use download_journal::{
31    IcSnapshotArtifactError, IcSnapshotArtifactWriter, IcSnapshotDataUploadPreparationError,
32    IcSnapshotUploadArtifactError,
33};
34pub use execution_progress::{ExecutionProgressPersistenceError, read_execution_progress};
35pub use execution_settlement::{
36    ExecutionSettlementCheckpointError, ExecutionSettlementPersistenceError,
37    read_execution_settlement, reconcile_execution_settlement, replay_execution_settlement,
38};
39pub(crate) use execution_workflow::read_execution_stage_occupancy;
40pub use execution_workflow::{
41    ExecutionStageCheckpointError, ExecutionStageGuard, ExecutionStagePreparationError,
42    ExecutionStageResumeError, ExecutionStageSettlementView, ExecutionWorkflowPersistenceError,
43    ExecutionWorkflowProgressError, ExecutionWorkflowSettlementView, create_execution_workflow,
44    read_complete_execution_workflow, read_complete_execution_workflow_locked,
45    read_execution_workflow, read_execution_workflow_progress,
46};
47pub use journal_lock::{JournalLock, JournalLockError};
48pub(crate) use json::read_json;
49pub use layout_lifetime::{BackupLayoutGuard, MAX_RESTORE_REFERENCE_BYTES};
50pub use operation_plan::{
51    OperationPlanPersistenceError, create_operation_plan, read_operation_plan,
52};
53pub use restore_reference_release::{
54    MAX_RESTORE_TERMINAL_BYTES, RestoreReferenceReleaseError, RestoreReferenceReleaseOutcome,
55    RestoreTerminalEvidenceProvider,
56};
57pub use restore_safety::{
58    RestoreSafetyPersistenceError, create_restore_safety_requirement,
59    read_restore_safety_requirement,
60};
61
62use crate::{model::artifacts::ChecksumError, ops::artifacts::ArtifactError};
63use std::io;
64use thiserror::Error;
65
66/// Typed local persistence failure.
67#[derive(Debug, Error)]
68pub enum PersistenceError {
69    /// A retired application requirement/obligation must be recovered by its original owner.
70    /// The record remains untouched; missing files never grant fresh authority.
71    #[error(
72        "retired application state at {path:?}; retain all evidence and recover with ic-backup 0.57.1 and the original integration before upgrading"
73    )]
74    RetiredApplicationState {
75        /// Existing retired declaration; no contents were inferred or converted.
76        path: std::path::PathBuf,
77    },
78    /// An existing layout path no longer denotes the held directory.
79    #[error("backup layout changed while held: {path:?}")]
80    LayoutChanged {
81        /// Resolved layout location.
82        path: std::path::PathBuf,
83    },
84    /// A retained dependency document or journal location has an unsafe entry type.
85    #[error("unsafe restore reference entry: {path:?}")]
86    InvalidRestoreReferences {
87        /// Rejected local path.
88        path: std::path::PathBuf,
89    },
90    /// Restore dependency validation or an immutable retention transition failed.
91    #[error(transparent)]
92    RestoreReference(#[from] crate::model::restore_references::RestoreReferenceError),
93    /// Parent preparation, record reads or other local filesystem IO failed.
94    /// Shared JSON publication failures retain their separate structured boundary.
95    #[error(transparent)]
96    Io(#[from] io::Error),
97    /// File publication failed, retaining producer/cleanup causes and visibility.
98    /// An after-publication failure leaves output visible and requires reconciliation.
99    #[error(transparent)]
100    Publication(#[from] ic_host_fs::durable::NamedWriteError<io::Error>),
101    /// JSON encoding or decoding failed.
102    #[error(transparent)]
103    Json(#[from] serde_json::Error),
104    /// A machine record exceeds its caller-selected byte bound.
105    #[error("record exceeds byte limit {limit}")]
106    RecordTooLarge {
107        /// Maximum accepted bytes.
108        limit: u64,
109    },
110    /// Artifact traversal or streaming failed.
111    #[error(transparent)]
112    Artifact(#[from] ArtifactError),
113    /// Verified bytes do not match the expected checksum.
114    #[error(transparent)]
115    Checksum(#[from] ChecksumError),
116    /// Publication requires distinct siblings in one directory.
117    #[error("artifact commit paths must be distinct siblings: {temporary}, {canonical}")]
118    ArtifactCommitPathMismatch {
119        /// Retained staging path.
120        temporary: String,
121        /// Canonical destination.
122        canonical: String,
123    },
124    /// Both staging and canonical trees exist; neither is overwritten.
125    #[error("artifact commit has conflicting paths: {temporary}, {canonical}")]
126    ArtifactCommitPathConflict {
127        /// Retained staging path.
128        temporary: String,
129        /// Existing canonical destination.
130        canonical: String,
131    },
132    /// Neither expected tree exists.
133    #[error("artifact commit is missing both paths: {temporary}, {canonical}")]
134    ArtifactCommitPathMissing {
135        /// Missing staging path.
136        temporary: String,
137        /// Missing canonical destination.
138        canonical: String,
139    },
140    /// Atomic no-replace directory publication is unsupported.
141    #[error("durable artifact publication is unsupported on {platform}")]
142    ArtifactCommitUnsupportedPlatform {
143        /// Host platform name.
144        platform: &'static str,
145    },
146    /// Publication found an unsafe tree entry.
147    #[error("unsupported artifact entry at {path}: {kind}")]
148    UnsupportedArtifactEntry {
149        /// Entry path.
150        path: String,
151        /// Observed entry kind.
152        kind: String,
153    },
154}
155
156// Diagnostic projection only; Host remains the regular-file admission owner.
157#[cfg(unix)]
158fn unsafe_entry_kind(path: &std::path::Path) -> &'static str {
159    match std::fs::symlink_metadata(path) {
160        Ok(metadata) if metadata.file_type().is_symlink() => "Symlink",
161        Ok(metadata) if metadata.is_dir() => "Directory",
162        _ => "Special",
163    }
164}