virtual_filesystem/physical_fs/
mod.rs1mod path_resolver;
2
3use crate::file::{DirEntry, File, Metadata, OpenOptions};
4use crate::physical_fs::path_resolver::{
5 PathResolver, SandboxedPathResolver, UnrestrictedPathResolver,
6};
7use crate::util::invalid_path;
8use crate::FileSystem;
9use normalize_path::NormalizePath;
10use std::fs;
11use std::marker::PhantomData;
12use std::path::{Path, PathBuf};
13
14pub struct PhysicalFSImpl<R: PathResolver> {
16 root: PathBuf,
17 _marker: PhantomData<R>,
18}
19
20pub type PhysicalFS = PhysicalFSImpl<UnrestrictedPathResolver>;
23pub type SandboxedPhysicalFS = PhysicalFSImpl<SandboxedPathResolver>;
27
28impl<R: PathResolver> PhysicalFSImpl<R> {
29 pub fn new<P: AsRef<Path>>(root: P) -> Self {
31 Self {
32 root: root.as_ref().normalize(),
33 _marker: PhantomData,
34 }
35 }
36}
37
38impl<R: PathResolver> FileSystem for PhysicalFSImpl<R> {
39 fn create_dir(&self, path: &str) -> crate::Result<()> {
40 fs::create_dir(R::resolve_path(&self.root, path)?.normalize())
41 }
42
43 fn metadata(&self, path: &str) -> crate::Result<Metadata> {
44 fs::metadata(R::resolve_path(&self.root, path)?.normalize()).map(Metadata::from)
45 }
46
47 fn open_file_options(&self, path: &str, options: &OpenOptions) -> crate::Result<Box<dyn File>> {
48 fs::OpenOptions::from(options)
49 .open(R::resolve_path(&self.root, path)?.normalize())
50 .map::<Box<dyn File>, _>(|file| Box::new(file))
51 }
52
53 fn read_dir(
54 &self,
55 path: &str,
56 ) -> crate::Result<Box<dyn Iterator<Item = crate::Result<DirEntry>>>> {
57 let resolved = R::resolve_path(&self.root, path)?;
58 let abs_root = if resolved.is_absolute() {
59 resolved.clone()
60 } else {
61 std::env::current_dir()?.join(&resolved).normalize()
62 };
63 let abs_root = abs_root.normalize();
64 Ok(Box::new(
65 fs::read_dir(&abs_root)?.map({
66 move |entry| {
67 entry.and_then({
68 |entry| {
69 Ok(DirEntry {
70 path: entry
72 .path()
73 .strip_prefix(&abs_root)
74 .map_err(|_| invalid_path())?
75 .into(),
76 metadata: entry.metadata()?.into(),
77 })
78 }
79 })
80 }
81 }),
82 ))
83 }
84
85 fn remove_dir(&self, path: &str) -> crate::Result<()> {
86 fs::remove_dir(R::resolve_path(&self.root, path)?.normalize())
87 }
88
89 fn remove_file(&self, path: &str) -> crate::Result<()> {
90 fs::remove_file(R::resolve_path(&self.root, path)?.normalize())
91 }
92}
93
94impl File for fs::File {
95 fn metadata(&self) -> crate::Result<Metadata> {
96 self.metadata().map(Metadata::from)
97 }
98}
99
100#[cfg(test)]
101mod test {
102 use crate::file::FileType;
103 use crate::physical_fs::{PhysicalFS, SandboxedPhysicalFS};
104 use crate::FileSystem;
105 use std::path::Path;
106
107 fn physical_fs<P: AsRef<Path>>(root: P) -> (PhysicalFS, SandboxedPhysicalFS) {
108 (
109 PhysicalFS::new(root.as_ref()),
110 SandboxedPhysicalFS::new(root.as_ref()),
111 )
112 }
113
114 #[test]
115 fn read_dir() {
116 let (unrestricted_fs, sandboxed_fs) = physical_fs("test");
117
118 let dir = sandboxed_fs.read_dir(".").unwrap();
120 assert!(dir.count() > 0);
121 let dir = unrestricted_fs.read_dir(".").unwrap();
122 assert!(dir.count() > 0);
123
124 assert!(sandboxed_fs.read_dir("..").is_err());
126 let dir = unrestricted_fs.read_dir("..").unwrap();
127 assert!(dir.count() > 0);
128
129 assert!(sandboxed_fs.read_dir("test/something/../../..").is_err());
131 let dir = unrestricted_fs.read_dir("test/something/../../..").unwrap();
132 assert!(dir.count() > 0);
133 }
134
135 #[test]
136 fn metadata() {
137 let (unrestricted_fs, sandboxed_fs) = physical_fs("test/folder_a");
138
139 let md = sandboxed_fs.metadata(".").unwrap();
141 assert_eq!(md.file_type, FileType::Directory);
142 assert_eq!(md.len, 0);
143 let md = unrestricted_fs.metadata(".").unwrap();
144 assert_eq!(md.file_type, FileType::Directory);
145 assert_eq!(md.len, 0);
146 let md = sandboxed_fs.metadata("file_a").unwrap();
147 assert_eq!(md.file_type, FileType::File);
148 assert_eq!(md.len, 6);
149 let md = unrestricted_fs.metadata("file_a").unwrap();
150 assert_eq!(md.file_type, FileType::File);
151 assert_eq!(md.len, 6);
152
153 assert!(sandboxed_fs.metadata("../deep_fs.zip").is_err());
155 let md = unrestricted_fs.metadata("../deep_fs.zip").unwrap();
156 assert_eq!(md.file_type, FileType::File);
157 assert_eq!(md.len, 2691);
158
159 assert!(sandboxed_fs.metadata("test/../../deep_fs.zip").is_err());
161 let md = unrestricted_fs.metadata("test/../../deep_fs.zip").unwrap();
162 assert_eq!(md.file_type, FileType::File);
163 assert_eq!(md.len, 2691);
164 }
165
166 #[test]
167 fn open_file() {
168 let (unrestricted_fs, sandboxed_fs) = physical_fs("test/folder_a");
169
170 let mut file = sandboxed_fs.open_file("file_a").unwrap();
172 let md = file.metadata().unwrap();
173 assert_eq!(md.file_type, FileType::File);
174 assert_eq!(md.len, 6);
175 assert_eq!(file.read_into_string().unwrap(), "file a");
176 let mut file = unrestricted_fs.open_file("file_a").unwrap();
177 let md = file.metadata().unwrap();
178 assert_eq!(md.file_type, FileType::File);
179 assert_eq!(md.len, 6);
180 assert_eq!(file.read_into_string().unwrap(), "file a");
181
182 assert!(sandboxed_fs.read_dir("../bad.tar.xz").is_err());
184 let mut file = unrestricted_fs.open_file("../bad.tar.xz").unwrap();
185 let md = file.metadata().unwrap();
186 assert_eq!(md.file_type, FileType::File);
187 assert_eq!(md.len, 4);
188 assert_eq!(file.read_into_string().unwrap(), "abcd");
189
190 assert!(sandboxed_fs.read_dir("test/../../bad.tar.xz").is_err());
192 let mut file = unrestricted_fs.open_file("test/../../bad.tar.xz").unwrap();
193 let md = file.metadata().unwrap();
194 assert_eq!(md.file_type, FileType::File);
195 assert_eq!(md.len, 4);
196 assert_eq!(file.read_into_string().unwrap(), "abcd");
197 }
198
199 #[test]
200 fn exists() {
201 let (unrestricted_fs, sandboxed_fs) = physical_fs("test");
202
203 assert!(unrestricted_fs.exists("").unwrap());
204 assert!(sandboxed_fs.exists("").unwrap());
205 assert!(unrestricted_fs.exists(".").unwrap());
206 assert!(sandboxed_fs.exists(".").unwrap());
207 assert!(unrestricted_fs.exists("///").unwrap());
208 assert!(sandboxed_fs.exists("///").unwrap());
209 assert!(unrestricted_fs.exists("\\\\").unwrap());
210 assert!(sandboxed_fs.exists("\\\\").unwrap());
211 assert!(unrestricted_fs.exists("folder_a").unwrap());
212 assert!(sandboxed_fs.exists("folder_a").unwrap());
213 assert!(!unrestricted_fs.exists("folder_c").unwrap());
214 assert!(!sandboxed_fs.exists("folder_c").unwrap());
215 assert!(unrestricted_fs.exists("bad.tar.xz").unwrap());
216 assert!(sandboxed_fs.exists("bad.tar.xz").unwrap());
217 assert!(unrestricted_fs.exists("../Cargo.toml").unwrap());
218 assert!(sandboxed_fs.exists("../Cargo.toml").is_err());
219 assert!(!unrestricted_fs.exists("../Cargo.toml2").unwrap());
220 assert!(unrestricted_fs.exists("folder_a/../../Cargo.toml").unwrap());
223 assert!(sandboxed_fs.exists("folder_a/../../Cargo.toml").is_err());
224 }
225}