forkfs/
lib.rs

1#![feature(let_chains)]
2#![feature(dir_entry_ext2)]
3#![allow(clippy::missing_errors_doc)]
4
5use std::{
6    fmt::{Debug, Display},
7    io,
8    path::PathBuf,
9};
10
11use error_stack::{Result, ResultExt};
12pub use run::run;
13pub use sessions::{
14    delete as delete_sessions, list as list_sessions, stop as stop_sessions, Op as SessionOperand,
15};
16
17mod run;
18mod sessions;
19
20#[derive(thiserror::Error, Debug)]
21pub enum Error {
22    #[error("An IO error occurred.")]
23    Io,
24    #[error("Invalid argument.")]
25    InvalidArgument,
26    #[error("ForkFS must be run as root.")]
27    NotRoot,
28    #[error("Session not found.")]
29    SessionNotFound,
30    #[error("Setup required.")]
31    SetupRequired,
32}
33
34fn get_sessions_dir() -> PathBuf {
35    let mut sessions_dir = dirs::cache_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
36    sessions_dir.push("forkfs");
37    sessions_dir
38}
39
40trait IoErr<Out> {
41    fn map_io_err_lazy<P: Display + Debug + Send + Sync + 'static>(
42        self,
43        f: impl FnOnce() -> P,
44    ) -> Out;
45
46    fn map_io_err<P: Display + Debug + Send + Sync + 'static>(self, p: P) -> Out;
47}
48
49impl<T> IoErr<Result<T, Error>> for io::Result<T> {
50    fn map_io_err_lazy<P: Display + Debug + Send + Sync + 'static>(
51        self,
52        f: impl FnOnce() -> P,
53    ) -> Result<T, Error> {
54        self.attach_printable_lazy(f).change_context(Error::Io)
55    }
56
57    fn map_io_err<P: Display + Debug + Send + Sync + 'static>(self, p: P) -> Result<T, Error> {
58        self.attach_printable(p).change_context(Error::Io)
59    }
60}
61
62impl<T> IoErr<Result<T, Error>> for std::result::Result<T, rustix::io::Errno> {
63    fn map_io_err_lazy<P: Display + Debug + Send + Sync + 'static>(
64        self,
65        f: impl FnOnce() -> P,
66    ) -> Result<T, Error> {
67        self.map_err(io::Error::from).map_io_err_lazy(f)
68    }
69
70    fn map_io_err<P: Display + Debug + Send + Sync + 'static>(self, p: P) -> Result<T, Error> {
71        self.map_err(io::Error::from).map_io_err(p)
72    }
73}
74
75mod path_undo {
76    use std::{
77        fmt::{Debug, Formatter},
78        ops::{Deref, DerefMut},
79        path::{Path, PathBuf},
80    };
81
82    pub struct TmpPath<'a>(&'a mut PathBuf);
83
84    impl<'a> TmpPath<'a> {
85        pub fn new(path: &'a mut PathBuf, child: impl AsRef<Path>) -> Self {
86            path.push(child);
87            Self(path)
88        }
89    }
90
91    impl<'a> Deref for TmpPath<'a> {
92        type Target = PathBuf;
93
94        fn deref(&self) -> &Self::Target {
95            self.0
96        }
97    }
98
99    impl<'a> AsRef<Path> for TmpPath<'a> {
100        fn as_ref(&self) -> &Path {
101            self.0
102        }
103    }
104
105    impl<'a> DerefMut for TmpPath<'a> {
106        fn deref_mut(&mut self) -> &mut Self::Target {
107            self.0
108        }
109    }
110
111    impl<'a> Debug for TmpPath<'a> {
112        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113            Debug::fmt(&**self, f)
114        }
115    }
116
117    impl<'a> Drop for TmpPath<'a> {
118        fn drop(&mut self) {
119            self.pop();
120        }
121    }
122}