1//! Module providing support for handling paths relative to a [`CheckedDir`].
2//!
3//! The underlying relative path of a [`RelKeyPath`] should not be manipulated directly.
4//! Instead, prefer converting it to an absolute path using
5//! [`checked_path`](RelKeyPath::checked_path) where possible.
6//! You may also use the `checked_op` macro to call [`CheckedDir`] functions on the path.
78use std::io;
9use std::path::{Path, PathBuf};
10use std::sync::Arc;
1112use fs_mistrust::CheckedDir;
13use tor_error::{ErrorKind, HasKind};
14use tor_key_forge::KeystoreItemType;
1516use crate::{ArtiPathUnavailableError, KeySpecifier};
1718/// The path of a key, relative to a [`CheckedDir`].
19///
20/// See the [module-level documentation](self) for a general overview.
21#[derive(Debug, Clone)]
22pub(super) struct RelKeyPath<'a> {
23/// The directory this path is relative to.
24dir: &'a CheckedDir,
25/// The relative path.
26path: PathBuf,
27}
2829impl<'a> RelKeyPath<'a> {
30/// Create a new [`RelKeyPath`] representing an `ArtiPath`.
31 ///
32 /// Returns an error if `key_spec` does not have an `ArtiPath`.
33pub(super) fn arti(
34 dir: &'a CheckedDir,
35 key_spec: &dyn KeySpecifier,
36 item_type: &KeystoreItemType,
37 ) -> Result<Self, ArtiPathUnavailableError> {
38let arti_path: String = key_spec.arti_path()?.into();
39let mut path = PathBuf::from(arti_path);
40 path.set_extension(item_type.arti_extension());
41Ok(Self { dir, path })
42 }
4344/// Create a new [`RelKeyPath`] from a `CheckedDir` and a relative path.
45#[cfg(feature = "ctor-keystore")]
46pub(super) fn from_parts(dir: &'a CheckedDir, path: PathBuf) -> Self {
47Self { dir, path }
48 }
4950/// Return the checked absolute path.
51pub(super) fn checked_path(&self) -> Result<PathBuf, FilesystemError> {
52let abs_path = self
53.dir
54 .join(&self.path)
55 .map_err(|err| FilesystemError::FsMistrust {
56 action: FilesystemAction::Read,
57 path: self.path.clone(),
58 err: err.into(),
59 })?;
6061Ok(abs_path)
62 }
6364/// Return this as an unchecked relative path.
65pub(super) fn rel_path_unchecked(&self) -> &Path {
66&self.path
67 }
6869/// Return the [`CheckedDir`] of this `RelKeyPath`.
70pub(super) fn checked_dir(&self) -> &CheckedDir {
71self.dir
72 }
73}
7475pub(crate) use internal::checked_op;
7677/// Private module for reexporting the `checked_op` macro.
78mod internal {
79/// Run operation `op` on a [`RelKeyPath`](super::RelKeyPath).
80 ///
81 /// `op` is an identifier that represents a [`CheckedDir`](fs_mistrust::CheckedDir) function.
82macro_rules! checked_op {
83 ($op:ident, $relpath:expr $(, $arg:expr)* ) => {{
84$relpath.checked_dir().$op($relpath.rel_path_unchecked(), $($arg,)* )
85 }}
86 }
8788pub(crate) use checked_op;
89}
9091/// An error that occurred while accessing the filesystem.
92#[derive(thiserror::Error, Debug, Clone)]
93pub(crate) enum FilesystemError {
94/// An IO error that occurred while accessing the filesystem.
95#[error("IO error on {path} while attempting to {action}")]
96Io {
97/// The action we were trying to perform.
98action: FilesystemAction,
99/// The path of the key we were trying to fetch.
100path: PathBuf,
101/// The underlying error.
102#[source]
103err: Arc<io::Error>,
104 },
105106/// Encountered an inaccessible path or invalid permissions.
107#[error("Inaccessible path or bad permissions on {path} while attempting to {action}")]
108FsMistrust {
109/// The action we were trying to perform.
110action: FilesystemAction,
111/// The path of the key we were trying to fetch.
112path: PathBuf,
113/// The underlying error.
114#[source]
115err: Arc<fs_mistrust::Error>,
116 },
117118/// An error due to encountering a directory or symlink at a key path.
119#[error("File at {0} is not a regular file")]
120NotARegularFile(PathBuf),
121}
122123/// The action that caused a [`FilesystemError`].
124#[derive(Copy, Clone, Debug, derive_more::Display)]
125pub(crate) enum FilesystemAction {
126/// Filesystem key store initialization.
127Init,
128/// Filesystem read
129Read,
130/// Filesystem write
131Write,
132/// Filesystem remove
133Remove,
134}
135136impl HasKind for FilesystemError {
137fn kind(&self) -> ErrorKind {
138use tor_persist::FsMistrustErrorExt as _;
139use FilesystemError as FE;
140141match self {
142 FE::Io { .. } => ErrorKind::KeystoreAccessFailed,
143 FE::FsMistrust { err, .. } => err.keystore_error_kind(),
144 FE::NotARegularFile(_) => ErrorKind::KeystoreCorrupted,
145 }
146 }
147}