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