Skip to main content

tor_keymgr/keystore/
fs_utils.rs

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.
7
8use std::io;
9use std::path::{Path, PathBuf};
10use std::sync::Arc;
11
12use fs_mistrust::CheckedDir;
13use tor_error::{ErrorKind, HasKind};
14use tor_key_forge::KeystoreItemType;
15
16use crate::{ArtiPathUnavailableError, KeySpecifier};
17
18/// 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.
24    dir: &'a CheckedDir,
25    /// The relative path.
26    path: PathBuf,
27}
28
29impl<'a> RelKeyPath<'a> {
30    /// Create a new [`RelKeyPath`] representing an `ArtiPath`.
31    ///
32    /// Returns an error if `key_spec` does not have an `ArtiPath`.
33    pub(super) fn arti(
34        dir: &'a CheckedDir,
35        key_spec: &dyn KeySpecifier,
36        item_type: &KeystoreItemType,
37    ) -> Result<Self, ArtiPathUnavailableError> {
38        let arti_path: String = key_spec.arti_path()?.into();
39        let mut path = PathBuf::from(arti_path);
40        path.set_extension(item_type.arti_extension());
41        Ok(Self { dir, path })
42    }
43
44    /// Create a new [`RelKeyPath`] from a `CheckedDir` and a relative path.
45    pub(super) fn from_parts(dir: &'a CheckedDir, path: PathBuf) -> Self {
46        Self { dir, path }
47    }
48
49    /// Return the checked absolute path.
50    pub(super) fn checked_path(&self) -> Result<PathBuf, FilesystemError> {
51        let abs_path = self
52            .dir
53            .join(&self.path)
54            .map_err(|err| FilesystemError::FsMistrust {
55                action: FilesystemAction::Read,
56                path: self.path.clone(),
57                err: err.into(),
58            })?;
59
60        Ok(abs_path)
61    }
62
63    /// Return this as an unchecked relative path.
64    pub(super) fn rel_path_unchecked(&self) -> &Path {
65        &self.path
66    }
67
68    /// Return the [`CheckedDir`] of this `RelKeyPath`.
69    pub(super) fn checked_dir(&self) -> &CheckedDir {
70        self.dir
71    }
72}
73
74pub(crate) use internal::checked_op;
75
76/// Private module for reexporting the `checked_op` macro.
77mod internal {
78    /// Run operation `op` on a [`RelKeyPath`](super::RelKeyPath).
79    ///
80    /// `op` is an identifier that represents a [`CheckedDir`](fs_mistrust::CheckedDir) function.
81    macro_rules! checked_op {
82        ($op:ident, $relpath:expr $(, $arg:expr)* ) => {{
83            $relpath.checked_dir().$op($relpath.rel_path_unchecked(),  $($arg,)* )
84        }}
85    }
86
87    pub(crate) use checked_op;
88}
89
90/// An error that occurred while accessing the filesystem.
91#[derive(thiserror::Error, Debug, Clone)]
92pub(crate) enum FilesystemError {
93    /// An IO error that occurred while accessing the filesystem.
94    #[error("IO error on {path} while attempting to {action}")]
95    Io {
96        /// The action we were trying to perform.
97        action: FilesystemAction,
98        /// The path of the key we were trying to fetch.
99        path: PathBuf,
100        /// The underlying error.
101        #[source]
102        err: Arc<io::Error>,
103    },
104
105    /// Encountered an inaccessible path or invalid permissions.
106    #[error("Inaccessible path or bad permissions on {path} while attempting to {action}")]
107    FsMistrust {
108        /// The action we were trying to perform.
109        action: FilesystemAction,
110        /// The path of the key we were trying to fetch.
111        path: PathBuf,
112        /// The underlying error.
113        #[source]
114        err: Arc<fs_mistrust::Error>,
115    },
116
117    /// An error due to encountering a directory or symlink at a key path.
118    #[error("File at {0} is not a regular file")]
119    NotARegularFile(PathBuf),
120}
121
122/// The action that caused a [`FilesystemError`].
123#[derive(Copy, Clone, Debug, derive_more::Display)]
124pub(crate) enum FilesystemAction {
125    /// Filesystem key store initialization.
126    Init,
127    /// Filesystem read
128    Read,
129    /// Filesystem write
130    Write,
131    /// Filesystem remove
132    Remove,
133}
134
135impl HasKind for FilesystemError {
136    fn kind(&self) -> ErrorKind {
137        use FilesystemError as FE;
138        use tor_persist::FsMistrustErrorExt as _;
139
140        match self {
141            FE::Io { .. } => ErrorKind::KeystoreAccessFailed,
142            FE::FsMistrust { err, .. } => err.keystore_error_kind(),
143            FE::NotARegularFile(_) => ErrorKind::KeystoreCorrupted,
144        }
145    }
146}