ic_backup/ops/artifacts/
mod.rs1#[cfg(test)]
4mod regressions;
5#[cfg(test)]
6mod tests;
7
8use crate::model::artifacts::ArtifactChecksumRecord;
9use ic_host_artifacts::artifact::tree::DirectoryChecksumError;
10#[cfg(any(unix, test))]
11use std::io::Read;
12use std::{
13 io,
14 path::{Path, PathBuf},
15};
16use thiserror::Error;
17
18pub(crate) fn checksum_directory(path: &Path) -> Result<ArtifactChecksumRecord, ArtifactError> {
19 #[cfg(unix)]
20 {
21 use ic_host_fs::read::tree::checksum::{TreeChecksumError, checksum_directory};
22 let digest = checksum_directory(path, u64::MAX).map_err(|error| match error {
23 TreeChecksumError::Io(error) => ArtifactError::Io(error),
24 TreeChecksumError::Input(error) => ArtifactError::Io(error.into()),
25 TreeChecksumError::Declaration(error) => ArtifactError::from(error),
26 TreeChecksumError::NonUtf8Path { path } => ArtifactError::NonUtf8Path { path },
27 TreeChecksumError::InvalidPath { path } => ArtifactError::UnsupportedEntry {
28 path: path.display().to_string(),
29 kind: "Unknown".into(),
30 },
31 TreeChecksumError::UnsupportedEntry { path, kind } => ArtifactError::UnsupportedEntry {
32 path: path.display().to_string(),
33 kind: format!("{kind:?}"),
34 },
35 })?;
36 Ok(ArtifactChecksumRecord::from_digest(*digest.as_bytes()))
37 }
38 #[cfg(not(unix))]
39 {
40 let _ = path;
41 Err(ArtifactError::UnsupportedPlatform(std::env::consts::OS))
42 }
43}
44
45#[cfg(any(unix, test))]
46pub(crate) fn checksum_reader(
55 reader: &mut impl Read,
56) -> Result<ArtifactChecksumRecord, ArtifactError> {
57 let identity =
58 ic_host_artifacts::artifact::hash_reader(reader, u64::MAX).map_err(io::Error::from)?;
59 Ok(ArtifactChecksumRecord::from_digest(
60 *identity.sha256.as_bytes(),
61 ))
62}
63
64pub(crate) fn checksum_relative_files(
65 files: Vec<(PathBuf, ArtifactChecksumRecord)>,
66) -> Result<ArtifactChecksumRecord, DirectoryChecksumError> {
67 let digest = ic_host_artifacts::artifact::tree::checksum_relative_files(
68 files
69 .into_iter()
70 .map(|(path, record)| (path, record.sha256()))
71 .collect(),
72 )?;
73 Ok(ArtifactChecksumRecord::from_digest(*digest.as_bytes()))
74}
75
76impl From<DirectoryChecksumError> for ArtifactError {
77 fn from(error: DirectoryChecksumError) -> Self {
78 match error {
79 DirectoryChecksumError::NonUtf8Path { path } => Self::NonUtf8Path { path },
80 error => Self::Io(io::Error::new(io::ErrorKind::InvalidData, error)),
81 }
82 }
83}
84
85#[derive(Debug, Error)]
87pub enum ArtifactError {
88 #[error("artifact path is not UTF-8: {path:?}")]
90 NonUtf8Path {
91 path: PathBuf,
93 },
94 #[error(transparent)]
96 Io(#[from] io::Error),
97 #[error("unsupported artifact entry at {path}: {kind}")]
99 UnsupportedEntry {
100 path: String,
102 kind: String,
104 },
105 #[error("secure artifact traversal is unsupported on platform {0}")]
107 UnsupportedPlatform(&'static str),
108}