vfs/impls/
altroot.rs

1//! A file system with its root in a particular directory of another filesystem
2
3use crate::{
4    error::VfsErrorKind, FileSystem, SeekAndRead, SeekAndWrite, VfsMetadata, VfsPath, VfsResult,
5};
6
7use std::time::SystemTime;
8
9/// Similar to a chroot but done purely by path manipulation
10///
11/// NOTE: This mechanism should only be used for convenience, NOT FOR SECURITY
12///
13/// Symlinks, hardlinks, remounts, side channels and other file system mechanisms can be exploited
14/// to circumvent this mechanism
15#[derive(Debug, Clone)]
16pub struct AltrootFS {
17    root: VfsPath,
18}
19
20impl AltrootFS {
21    /// Create a new root FileSystem at the given virtual path
22    pub fn new(root: VfsPath) -> Self {
23        AltrootFS { root }
24    }
25}
26
27impl AltrootFS {
28    #[allow(clippy::manual_strip)] // strip prefix manually for MSRV 1.32
29    fn path(&self, path: &str) -> VfsResult<VfsPath> {
30        if path.is_empty() {
31            return Ok(self.root.clone());
32        }
33        if path.starts_with('/') {
34            return self.root.join(&path[1..]);
35        }
36        self.root.join(path)
37    }
38}
39
40impl FileSystem for AltrootFS {
41    fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>> {
42        self.path(path)?
43            .read_dir()
44            .map(|result| result.map(|path| path.filename()))
45            .map(|entries| Box::new(entries) as Box<dyn Iterator<Item = String> + Send>)
46    }
47
48    fn create_dir(&self, path: &str) -> VfsResult<()> {
49        self.path(path)?.create_dir()
50    }
51
52    fn open_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndRead + Send>> {
53        self.path(path)?.open_file()
54    }
55
56    fn create_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
57        self.path(path)?.create_file()
58    }
59
60    fn append_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> {
61        self.path(path)?.append_file()
62    }
63
64    fn metadata(&self, path: &str) -> VfsResult<VfsMetadata> {
65        self.path(path)?.metadata()
66    }
67
68    fn set_creation_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
69        self.path(path)?.set_creation_time(time)
70    }
71
72    fn set_modification_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
73        self.path(path)?.set_modification_time(time)
74    }
75
76    fn set_access_time(&self, path: &str, time: SystemTime) -> VfsResult<()> {
77        self.path(path)?.set_access_time(time)
78    }
79
80    fn exists(&self, path: &str) -> VfsResult<bool> {
81        self.path(path)
82            .map(|path| path.exists())
83            .unwrap_or(Ok(false))
84    }
85
86    fn remove_file(&self, path: &str) -> VfsResult<()> {
87        self.path(path)?.remove_file()
88    }
89
90    fn remove_dir(&self, path: &str) -> VfsResult<()> {
91        self.path(path)?.remove_dir()
92    }
93
94    fn copy_file(&self, src: &str, dest: &str) -> VfsResult<()> {
95        if dest.is_empty() {
96            return Err(VfsErrorKind::NotSupported.into());
97        }
98        self.path(src)?.copy_file(&self.path(dest)?)
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use crate::MemoryFS;
106    test_vfs!({
107        let memory_root: VfsPath = MemoryFS::new().into();
108        let altroot_path = memory_root.join("altroot").unwrap();
109        altroot_path.create_dir().unwrap();
110        AltrootFS::new(altroot_path)
111    });
112
113    #[test]
114    fn parent() {
115        let memory_root: VfsPath = MemoryFS::new().into();
116        let altroot_path = memory_root.join("altroot").unwrap();
117        altroot_path.create_dir().unwrap();
118        let altroot: VfsPath = AltrootFS::new(altroot_path.clone()).into();
119        assert_eq!(altroot.parent(), altroot.root());
120        assert_eq!(altroot_path.parent(), memory_root);
121    }
122}
123
124#[cfg(test)]
125mod tests_physical {
126    use super::*;
127    use crate::PhysicalFS;
128    test_vfs!({
129        let temp_dir = std::env::temp_dir();
130        let dir = temp_dir.join(uuid::Uuid::new_v4().to_string());
131        std::fs::create_dir_all(&dir).unwrap();
132
133        let physical_root: VfsPath = PhysicalFS::new(dir).into();
134        let altroot_path = physical_root.join("altroot").unwrap();
135        altroot_path.create_dir().unwrap();
136        AltrootFS::new(altroot_path)
137    });
138
139    test_vfs_readonly!({
140        let physical_root: VfsPath = PhysicalFS::new("test").into();
141        let altroot_path = physical_root.join("test_directory").unwrap();
142        AltrootFS::new(altroot_path)
143    });
144}