Skip to main content

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