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)?)
41 }
42
43 fn metadata(&self, path: &str) -> crate::Result<Metadata> {
44 fs::metadata(R::resolve_path(&self.root, path)?).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)?)
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 Ok(Box::new(
58 fs::read_dir(R::resolve_path(&self.root, path)?)?.map({
59 let root = self.root.clone();
60 move |entry| {
61 entry.and_then({
62 |entry| {
63 Ok(DirEntry {
64 path: entry
66 .path()
67 .strip_prefix(&root)
68 .map_err(|_| invalid_path())?
69 .into(),
70 metadata: entry.metadata()?.into(),
71 })
72 }
73 })
74 }
75 }),
76 ))
77 }
78
79 fn remove_dir(&self, path: &str) -> crate::Result<()> {
80 fs::remove_dir(R::resolve_path(&self.root, path)?)
81 }
82
83 fn remove_file(&self, path: &str) -> crate::Result<()> {
84 fs::remove_file(R::resolve_path(&self.root, path)?)
85 }
86}
87
88impl File for fs::File {
89 fn metadata(&self) -> crate::Result<Metadata> {
90 self.metadata().map(Metadata::from)
91 }
92}
93
94#[cfg(test)]
95mod test {
96 use crate::file::FileType;
97 use crate::physical_fs::{PhysicalFS, SandboxedPhysicalFS};
98 use crate::FileSystem;
99 use std::path::Path;
100
101 fn physical_fs<P: AsRef<Path>>(root: P) -> (PhysicalFS, SandboxedPhysicalFS) {
102 (
103 PhysicalFS::new(root.as_ref()),
104 SandboxedPhysicalFS::new(root.as_ref()),
105 )
106 }
107
108 #[test]
109 fn read_dir() {
110 let (unrestricted_fs, sandboxed_fs) = physical_fs("test");
111
112 let dir = sandboxed_fs.read_dir(".").unwrap();
114 assert!(dir.count() > 0);
115 let dir = unrestricted_fs.read_dir(".").unwrap();
116 assert!(dir.count() > 0);
117
118 assert!(sandboxed_fs.read_dir("..").is_err());
120 let dir = unrestricted_fs.read_dir("..").unwrap();
121 assert!(dir.count() > 0);
122
123 assert!(sandboxed_fs.read_dir("test/something/../../..").is_err());
125 let dir = unrestricted_fs.read_dir("test/something/../../..").unwrap();
126 assert!(dir.count() > 0);
127 }
128
129 #[test]
130 fn metadata() {
131 let (unrestricted_fs, sandboxed_fs) = physical_fs("test/folder_a");
132
133 let md = sandboxed_fs.metadata(".").unwrap();
135 assert_eq!(md.file_type, FileType::Directory);
136 assert_eq!(md.len, 0);
137 let md = unrestricted_fs.metadata(".").unwrap();
138 assert_eq!(md.file_type, FileType::Directory);
139 assert_eq!(md.len, 0);
140 let md = sandboxed_fs.metadata("file_a").unwrap();
141 assert_eq!(md.file_type, FileType::File);
142 assert_eq!(md.len, 6);
143 let md = unrestricted_fs.metadata("file_a").unwrap();
144 assert_eq!(md.file_type, FileType::File);
145 assert_eq!(md.len, 6);
146
147 assert!(sandboxed_fs.metadata("../deep_fs.zip").is_err());
149 let md = unrestricted_fs.metadata("../deep_fs.zip").unwrap();
150 assert_eq!(md.file_type, FileType::File);
151 assert_eq!(md.len, 2691);
152
153 assert!(sandboxed_fs.metadata("test/../../deep_fs.zip").is_err());
155 let md = unrestricted_fs.metadata("test/../../deep_fs.zip").unwrap();
156 assert_eq!(md.file_type, FileType::File);
157 assert_eq!(md.len, 2691);
158 }
159
160 #[test]
161 fn open_file() {
162 let (unrestricted_fs, sandboxed_fs) = physical_fs("test/folder_a");
163
164 let mut file = sandboxed_fs.open_file("file_a").unwrap();
166 let md = file.metadata().unwrap();
167 assert_eq!(md.file_type, FileType::File);
168 assert_eq!(md.len, 6);
169 assert_eq!(file.read_into_string().unwrap(), "file a");
170 let mut file = unrestricted_fs.open_file("file_a").unwrap();
171 let md = file.metadata().unwrap();
172 assert_eq!(md.file_type, FileType::File);
173 assert_eq!(md.len, 6);
174 assert_eq!(file.read_into_string().unwrap(), "file a");
175
176 assert!(sandboxed_fs.read_dir("../bad.tar.xz").is_err());
178 let mut file = unrestricted_fs.open_file("../bad.tar.xz").unwrap();
179 let md = file.metadata().unwrap();
180 assert_eq!(md.file_type, FileType::File);
181 assert_eq!(md.len, 4);
182 assert_eq!(file.read_into_string().unwrap(), "abcd");
183
184 assert!(sandboxed_fs.read_dir("test/../../bad.tar.xz").is_err());
186 let mut file = unrestricted_fs.open_file("test/../../bad.tar.xz").unwrap();
187 let md = file.metadata().unwrap();
188 assert_eq!(md.file_type, FileType::File);
189 assert_eq!(md.len, 4);
190 assert_eq!(file.read_into_string().unwrap(), "abcd");
191 }
192
193 #[test]
194 fn exists() {
195 let (unrestricted_fs, sandboxed_fs) = physical_fs("test");
196
197 assert!(unrestricted_fs.exists("").unwrap());
198 assert!(sandboxed_fs.exists("").unwrap());
199 assert!(unrestricted_fs.exists(".").unwrap());
200 assert!(sandboxed_fs.exists(".").unwrap());
201 assert!(unrestricted_fs.exists("///").unwrap());
202 assert!(sandboxed_fs.exists("///").unwrap());
203 assert!(unrestricted_fs.exists("\\\\").unwrap());
204 assert!(sandboxed_fs.exists("\\\\").unwrap());
205 assert!(unrestricted_fs.exists("folder_a").unwrap());
206 assert!(sandboxed_fs.exists("folder_a").unwrap());
207 assert!(!unrestricted_fs.exists("folder_c").unwrap());
208 assert!(!sandboxed_fs.exists("folder_c").unwrap());
209 assert!(unrestricted_fs.exists("bad.tar.xz").unwrap());
210 assert!(sandboxed_fs.exists("bad.tar.xz").unwrap());
211 assert!(unrestricted_fs.exists("../Cargo.toml").unwrap());
212 assert!(sandboxed_fs.exists("../Cargo.toml").is_err());
213 assert!(!unrestricted_fs.exists("../Cargo.toml2").unwrap());
214 assert!(unrestricted_fs.exists("folder_a/../../Cargo.toml").unwrap());
217 assert!(sandboxed_fs.exists("folder_a/../../Cargo.toml").is_err());
218 }
219}