alpm_mtree/mtree/
path_validation_error.rs

1//! Error handling related to path validation based on [`Mtree`] data.
2
3use std::{fmt::Display, path::PathBuf};
4
5use alpm_types::Sha256Checksum;
6use fluent_i18n::t;
7
8#[cfg(doc)]
9use crate::Mtree;
10
11/// A list of errors that may occur when comparing [`Mtree`] data with paths inside a `base_dir`.
12///
13/// Tracks a `base_dir` whose files are compared to [`Mtree`] data.
14/// Also tracks a list of zero or more [`PathValidationError`]s that occurred when validating paths
15/// inside `base_dir` by comparing it with [`Mtree`] data.
16///
17/// After initialization, [`append`][`PathValidationErrors::append`] can be used to add any
18/// errors to this struct that occurred during validation.
19/// After validation (which is considered an infallible action), calling
20/// [`check`][`PathValidationErrors::check`] returns an error if any errors have been collected
21/// during validation.
22#[derive(Debug, Default, thiserror::Error)]
23pub struct PathValidationErrors {
24    base_dir: PathBuf,
25    errors: Vec<PathValidationError>,
26}
27
28impl PathValidationErrors {
29    /// Creates a new [`PathValidationErrors`] for a directory.
30    pub fn new(base_dir: PathBuf) -> Self {
31        Self {
32            base_dir,
33            errors: Vec::new(),
34        }
35    }
36
37    /// Appends a list of [`PathValidationError`]s to `self.errors`.
38    pub fn append(&mut self, other: &mut Vec<PathValidationError>) {
39        self.errors.append(other);
40    }
41
42    /// Checks if errors have been appended and consumes `self`.
43    ///
44    /// # Errors
45    ///
46    /// Returns an error if one or more errors have been appended.
47    pub fn check(self) -> Result<(), crate::Error> {
48        if !self.errors.is_empty() {
49            return Err(crate::Error::PathValidation(self));
50        }
51
52        Ok(())
53    }
54}
55
56impl Display for PathValidationErrors {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        write!(
59            f,
60            "{}",
61            t!("error-path-validation-errors", {
62                "base_dir" => self.base_dir.display().to_string(),
63                "errors" => self
64                    .errors
65                    .iter()
66                    .map(|e| e.to_string())
67                    .collect::<Vec<_>>()
68                    .join("\n")
69            })
70        )
71    }
72}
73
74/// The error that can occur when comparing [`Mtree`] paths with paths on a file system.
75#[derive(Debug, thiserror::Error)]
76pub enum PathValidationError {
77    /// Alpm-common error.
78    #[error("{msg}", msg = t!("error-alpm-common", { "source" => .0.to_string() }))]
79    AlpmCommon(#[from] alpm_common::Error),
80
81    /// Unable to create hash digest for a path.
82    #[error("{msg}", msg = t!("error-create-hash-digest", {
83        "path" => path.display().to_string(),
84        "source" => source.to_string()
85    }))]
86    CreateHashDigest {
87        /// The path for which a hash digest can not be created.
88        path: PathBuf,
89        /// The source error.
90        source: std::io::Error,
91    },
92
93    /// The hash digest of a path in the ALPM-MTREE data does not match that of the corresponding
94    /// on-disk file.
95    #[error("{msg}", msg = t!("error-path-digest-mismatch", {
96        "mtree_path" => mtree_path.display().to_string(),
97        "mtree_digest" => mtree_digest.to_string(),
98        "path" => path.display().to_string(),
99        "path_digest" => path_digest.to_string()
100    }))]
101    PathDigestMismatch {
102        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
103        mtree_path: PathBuf,
104        /// The size of the path according to ALPM-MTREE data.
105        mtree_digest: Sha256Checksum,
106        /// The on-disk path, that does not match the size of the ALPM-MTREE data.
107        path: PathBuf,
108        /// The on-disk path, that does not match the size of the ALPM-MTREE data.
109        path_digest: Sha256Checksum,
110    },
111
112    /// The GID of a path in the ALPM-MTREE metadata does not match that of the corresponding
113    /// on-disk file.
114    #[error("{msg}", msg = t!("error-path-gid-mismatch", {
115        "mtree_path" => mtree_path.display().to_string(),
116        "mtree_gid" => mtree_gid.to_string(),
117        "path" => path.display().to_string(),
118        "path_gid" => path_gid.to_string()
119    }))]
120    PathGidMismatch {
121        /// The path in the ALPM-MTREE data that has a differing GID from that of the on-disk file.
122        mtree_path: PathBuf,
123        /// The GID recorded in the ALPM-MTREE data for `mtree_path`.
124        mtree_gid: u32,
125        /// The on-disk path, that has a differing GID from the ALPM-MTREE data.
126        path: PathBuf,
127        /// The GID used for `path`.
128        path_gid: u32,
129    },
130
131    /// The metadata for a path can not be retrieved.
132    #[error("{msg}", msg = t!("error-path-metadata", {
133        "path" => path.display().to_string(),
134        "source" => source.to_string()
135    }))]
136    PathMetadata {
137        /// The on-disk path for which metadata can not be retrieved.
138        path: PathBuf,
139        /// The source Error.
140        source: std::io::Error,
141    },
142
143    /// A path does not match what it is supposed to be.
144    #[error("{msg}", msg = t!("error-path-mismatch", {
145        "mtree_path" => mtree_path.display().to_string(),
146        "path" => path.display().to_string()
147    }))]
148    PathMismatch {
149        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
150        mtree_path: PathBuf,
151        /// The on-disk path, that does not match that of the ALPM-MTREE data.
152        path: PathBuf,
153    },
154
155    /// An on-disk path does not exist.
156    #[error("{msg}", msg = t!("error-path-missing", {
157        "mtree_path" => mtree_path.display().to_string(),
158        "path" => path.display().to_string()
159    }))]
160    PathMissing {
161        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
162        mtree_path: PathBuf,
163        /// The absolute on-disk path, that does not exist.
164        path: PathBuf,
165    },
166
167    /// The mode of a path in the ALPM-MTREE metadata does not match that of the corresponding
168    /// on-disk file.
169    #[error("{msg}", msg = t!("error-path-mode-mismatch", {
170        "mtree_path" => mtree_path.display().to_string(),
171        "mtree_mode" => mtree_mode,
172        "path" => path.display().to_string(),
173        "path_mode" => path_mode
174    }))]
175    PathModeMismatch {
176        /// The path in the ALPM-MTREE data that has a differing mode from that of the on-disk
177        /// file.
178        mtree_path: PathBuf,
179        /// The mode recorded in the ALPM-MTREE data for `mtree_path`.
180        mtree_mode: String,
181        /// The on-disk path, that has a differing mode from that of the ALPM-MTREE data.
182        path: PathBuf,
183        /// The mode used for `path`.
184        path_mode: String,
185    },
186
187    /// An on-disk path is not a directory.
188    #[error("{msg}", msg = t!("error-path-not-a-dir", {
189        "mtree_path" => mtree_path.display().to_string(),
190        "path" => path.display().to_string()
191    }))]
192    PathNotADir {
193        /// The path in the ALPM-MTREE data requiring `path` to be a directory.
194        mtree_path: PathBuf,
195        /// The absolute on-disk path, that is not a directory.
196        path: PathBuf,
197    },
198
199    /// An on-disk path is not a file.
200    #[error("{msg}", msg = t!("error-path-not-a-file", {
201        "mtree_path" => mtree_path.display().to_string(),
202        "path" => path.display().to_string()
203    }))]
204    PathNotAFile {
205        /// The path in the ALPM-MTREE data requiring `path` to be a file.
206        mtree_path: PathBuf,
207        /// The absolute on-disk path, that is not a file.
208        path: PathBuf,
209    },
210
211    /// The size of a path in the ALPM-MTREE metadata does not match the size of the corresponding
212    /// on-disk file.
213    #[error("{msg}", msg = t!("error-path-size-mismatch", {
214        "mtree_path" => mtree_path.display().to_string(),
215        "mtree_size" => mtree_size.to_string(),
216        "path" => path.display().to_string(),
217        "path_size" => path_size.to_string()
218    }))]
219    PathSizeMismatch {
220        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
221        mtree_path: PathBuf,
222        /// The size of the path according to ALPM-MTREE data.
223        mtree_size: u64,
224        /// The on-disk path, that does not match the size of the ALPM-MTREE data.
225        path: PathBuf,
226        /// The on-disk path, that does not match the size of the ALPM-MTREE data.
227        path_size: u64,
228    },
229
230    /// A path does not match what it is supposed to be.
231    #[error("{msg}", msg = t!("error-path-symlink-mismatch", {
232        "mtree_path" => mtree_path.display().to_string(),
233        "mtree_link_path" => mtree_link_path.display().to_string(),
234        "path" => path.display().to_string(),
235        "link_path" => link_path.display().to_string()
236    }))]
237    PathSymlinkMismatch {
238        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
239        mtree_path: PathBuf,
240        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
241        mtree_link_path: PathBuf,
242        /// The on-disk path, that does not match that of the ALPM-MTREE data.
243        path: PathBuf,
244        /// The on-disk path, that does not match that of the ALPM-MTREE data.
245        link_path: PathBuf,
246    },
247
248    /// The time of a path in the ALPM-MTREE metadata does not match the time of the corresponding
249    /// on-disk file.
250    #[error("{msg}", msg = t!("error-path-time-mismatch", {
251        "mtree_path" => mtree_path.display().to_string(),
252        "mtree_time" => mtree_time.to_string(),
253        "path" => path.display().to_string(),
254        "path_time" => path_time.to_string()
255    }))]
256    PathTimeMismatch {
257        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
258        mtree_path: PathBuf,
259        /// The size of the path according to ALPM-MTREE data.
260        mtree_time: i64,
261        /// The on-disk path, that does not match the size of the ALPM-MTREE data.
262        path: PathBuf,
263        /// The on-disk path, that does not match the size of the ALPM-MTREE data.
264        path_time: i64,
265    },
266
267    /// The UID of a path in the ALPM-MTREE metadata does not match that of the corresponding
268    /// on-disk file.
269    #[error("{msg}", msg = t!("error-path-uid-mismatch", {
270        "mtree_path" => mtree_path.display().to_string(),
271        "mtree_uid" => mtree_uid.to_string(),
272        "path" => path.display().to_string(),
273        "path_uid" => path_uid.to_string()
274    }))]
275    PathUidMismatch {
276        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
277        mtree_path: PathBuf,
278        /// The UID recorded in the ALPM-MTREE data.
279        mtree_uid: u32,
280        /// The on-disk path, that does not match the size of the ALPM-MTREE data.
281        path: PathBuf,
282        /// The UID used for `path`.
283        path_uid: u32,
284    },
285
286    /// Unable to read a link.
287    #[error("{msg}", msg = t!("error-read-link", {
288        "path" => path.display().to_string(),
289        "mtree_path" => mtree_path.display().to_string(),
290        "source" => source.to_string()
291    }))]
292    ReadLink {
293        /// The path for a symlink on the file system which can not be read.
294        path: PathBuf,
295        /// The path in the ALPM-MTREE data that does not have a matching path on disk.
296        mtree_path: PathBuf,
297        /// The source error
298        source: std::io::Error,
299    },
300
301    /// There are file system paths for which no matching ALPM-MTREE paths exist.
302    #[error("{msg}\n", msg = t!("error-unmatched-fs-paths", {
303        "paths" => paths.iter().map(|p| format!("{p:?}")).collect::<Vec<_>>().join("\n")
304    }))]
305    UnmatchedFileSystemPaths {
306        /// The list of file system paths for which no matching ALPM-MTREE paths exist.
307        paths: Vec<PathBuf>,
308    },
309
310    /// There are ALPM-MTREE paths for which no matching file system paths exist.
311    #[error("{msg}", msg = t!("error-unmatched-mtree-paths", {
312        "paths" => paths.iter().map(|p| format!("{p:?}")).collect::<Vec<_>>().join("\n")
313    }))]
314    UnmatchedMtreePaths {
315        /// The list of ALPM-MTREE paths for which no matching file system paths exist.
316        paths: Vec<PathBuf>,
317    },
318}