use super::{DownloadJournalError, DownloadJournalGuard, check_size};
use crate::{
model::{
artifacts::ChecksumError,
download_journal::{DownloadJournalRecord, MAX_DOWNLOAD_JOURNAL_BYTES},
operation_plan::OperationPlanRecord,
},
ops::{
artifacts::{ArtifactError, checksum_directory},
persistence::{
OperationPlanPersistenceError, PersistenceError, read_json, read_operation_plan,
},
},
policy::download_integrity::{DownloadIntegrityPolicyError, DurableDownloadView, validate},
};
use thiserror::Error;
impl DownloadJournalGuard<'_> {
pub fn verify_durable_artifacts<'a>(
&'a self,
plan: &'a OperationPlanRecord,
) -> Result<DurableDownloadView<'a>, DownloadIntegrityError> {
self.check_usable()?;
read_operation_plan(self.layout, &plan.digest())?;
self.require_unchanged_integrity_journal()?;
let view = validate(plan, &self.record)?;
self.check_artifact_parent()?;
for artifact in view.artifacts() {
let path = self.layout.root().join(artifact.artifact().artifact_path());
checksum_directory(&path)?.verify(artifact.checksum().hash())?;
}
self.check_usable()?;
read_operation_plan(self.layout, &plan.digest())?;
self.require_unchanged_integrity_journal()?;
Ok(view)
}
pub(super) fn require_unchanged_integrity_journal(&self) -> Result<(), DownloadIntegrityError> {
let retained: DownloadJournalRecord = read_json(&self.path(), MAX_DOWNLOAD_JOURNAL_BYTES)?;
check_size(&retained)?;
if retained != self.record {
return Err(DownloadIntegrityError::JournalChanged);
}
Ok(())
}
}
#[derive(Debug, Error)]
pub enum DownloadIntegrityError {
#[error("retained download journal changed during integrity verification")]
JournalChanged,
#[error(transparent)]
Journal(#[from] DownloadJournalError),
#[error(transparent)]
Plan(#[from] OperationPlanPersistenceError),
#[error(transparent)]
Policy(#[from] DownloadIntegrityPolicyError),
#[error(transparent)]
Persistence(#[from] PersistenceError),
#[error(transparent)]
Artifact(#[from] ArtifactError),
#[error(transparent)]
Checksum(#[from] ChecksumError),
}
#[cfg(all(test, unix))]
mod tests;