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

            
8
use std::io;
9
use std::path::{Path, PathBuf};
10
use std::sync::Arc;
11

            
12
use fs_mistrust::CheckedDir;
13
use tor_error::{ErrorKind, HasKind};
14
use tor_key_forge::KeystoreItemType;
15

            
16
use 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)]
22
pub(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

            
29
impl<'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
35860
    pub(super) fn arti(
34
35860
        dir: &'a CheckedDir,
35
35860
        key_spec: &dyn KeySpecifier,
36
35860
        item_type: &KeystoreItemType,
37
35860
    ) -> Result<Self, ArtiPathUnavailableError> {
38
35860
        let arti_path: String = key_spec.arti_path()?.into();
39
35860
        let mut path = PathBuf::from(arti_path);
40
35860
        path.set_extension(item_type.arti_extension());
41
35860
        Ok(Self { dir, path })
42
35860
    }
43

            
44
    /// Create a new [`RelKeyPath`] from a `CheckedDir` and a relative path.
45
    #[cfg(feature = "ctor-keystore")]
46
3044
    pub(super) fn from_parts(dir: &'a CheckedDir, path: PathBuf) -> Self {
47
3044
        Self { dir, path }
48
3044
    }
49

            
50
    /// Return the checked absolute path.
51
25040
    pub(super) fn checked_path(&self) -> Result<PathBuf, FilesystemError> {
52
25040
        let abs_path = self
53
25040
            .dir
54
25040
            .join(&self.path)
55
25040
            .map_err(|err| FilesystemError::FsMistrust {
56
                action: FilesystemAction::Read,
57
                path: self.path.clone(),
58
                err: err.into(),
59
            })?;
60

            
61
25040
        Ok(abs_path)
62
25040
    }
63

            
64
    /// Return this as an unchecked relative path.
65
44898
    pub(super) fn rel_path_unchecked(&self) -> &Path {
66
44898
        &self.path
67
44898
    }
68

            
69
    /// Return the [`CheckedDir`] of this `RelKeyPath`.
70
37938
    pub(super) fn checked_dir(&self) -> &CheckedDir {
71
37938
        self.dir
72
37938
    }
73
}
74

            
75
pub(crate) use internal::checked_op;
76

            
77
/// Private module for reexporting the `checked_op` macro.
78
mod internal {
79
    /// Run operation `op` on a [`RelKeyPath`](super::RelKeyPath).
80
    ///
81
    /// `op` is an identifier that represents a [`CheckedDir`](fs_mistrust::CheckedDir) function.
82
    macro_rules! checked_op {
83
        ($op:ident, $relpath:expr $(, $arg:expr)* ) => {{
84
            $relpath.checked_dir().$op($relpath.rel_path_unchecked(),  $($arg,)* )
85
        }}
86
    }
87

            
88
    pub(crate) use checked_op;
89
}
90

            
91
/// An error that occurred while accessing the filesystem.
92
#[derive(thiserror::Error, Debug, Clone)]
93
pub(crate) enum FilesystemError {
94
    /// An IO error that occurred while accessing the filesystem.
95
    #[error("IO error on {path} while attempting to {action}")]
96
    Io {
97
        /// The action we were trying to perform.
98
        action: FilesystemAction,
99
        /// The path of the key we were trying to fetch.
100
        path: PathBuf,
101
        /// The underlying error.
102
        #[source]
103
        err: Arc<io::Error>,
104
    },
105

            
106
    /// Encountered an inaccessible path or invalid permissions.
107
    #[error("Inaccessible path or bad permissions on {path} while attempting to {action}")]
108
    FsMistrust {
109
        /// The action we were trying to perform.
110
        action: FilesystemAction,
111
        /// The path of the key we were trying to fetch.
112
        path: PathBuf,
113
        /// The underlying error.
114
        #[source]
115
        err: Arc<fs_mistrust::Error>,
116
    },
117

            
118
    /// An error due to encountering a directory or symlink at a key path.
119
    #[error("File at {0} is not a regular file")]
120
    NotARegularFile(PathBuf),
121
}
122

            
123
/// The action that caused a [`FilesystemError`].
124
#[derive(Copy, Clone, Debug, derive_more::Display)]
125
pub(crate) enum FilesystemAction {
126
    /// Filesystem key store initialization.
127
    Init,
128
    /// Filesystem read
129
    Read,
130
    /// Filesystem write
131
    Write,
132
    /// Filesystem remove
133
    Remove,
134
}
135

            
136
impl HasKind for FilesystemError {
137
    fn kind(&self) -> ErrorKind {
138
        use FilesystemError as FE;
139
        use tor_persist::FsMistrustErrorExt as _;
140

            
141
        match self {
142
            FE::Io { .. } => ErrorKind::KeystoreAccessFailed,
143
            FE::FsMistrust { err, .. } => err.keystore_error_kind(),
144
            FE::NotARegularFile(_) => ErrorKind::KeystoreCorrupted,
145
        }
146
    }
147
}