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