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}